summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-03-28 22:27:35 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-03-28 22:27:35 +0300
commit02e2af20f4f9f2aa0c84e9a30a35c02f0fbb7daa (patch)
treed126449a7d2ea2270627183f7cebd726fbe56a9d /drivers
parentff61bc81b3feebcef4d0431a92e2e40e8d4fe8b3 (diff)
parent37fd83916da2e4cae03d350015c82a67b1b334c4 (diff)
downloadlinux-02e2af20f4f9f2aa0c84e9a30a35c02f0fbb7daa.tar.xz
Merge tag 'char-misc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc and other driver updates from Greg KH: "Here is the big set of char/misc and other small driver subsystem updates for 5.18-rc1. Included in here are merges from driver subsystems which contain: - iio driver updates and new drivers - fsi driver updates - fpga driver updates - habanalabs driver updates and support for new hardware - soundwire driver updates and new drivers - phy driver updates and new drivers - coresight driver updates - icc driver updates Individual changes include: - mei driver updates - interconnect driver updates - new PECI driver subsystem added - vmci driver updates - lots of tiny misc/char driver updates All of these have been in linux-next for a while with no reported problems" * tag 'char-misc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (556 commits) firmware: google: Properly state IOMEM dependency kgdbts: fix return value of __setup handler firmware: sysfb: fix platform-device leak in error path firmware: stratix10-svc: add missing callback parameter on RSU arm64: dts: qcom: add non-secure domain property to fastrpc nodes misc: fastrpc: Add dma handle implementation misc: fastrpc: Add fdlist implementation misc: fastrpc: Add helper function to get list and page misc: fastrpc: Add support to secure memory map dt-bindings: misc: add fastrpc domain vmid property misc: fastrpc: check before loading process to the DSP misc: fastrpc: add secure domain support dt-bindings: misc: add property to support non-secure DSP misc: fastrpc: Add support to get DSP capabilities misc: fastrpc: add support for FASTRPC_IOCTL_MEM_MAP/UNMAP misc: fastrpc: separate fastrpc device from channel context dt-bindings: nvmem: brcm,nvram: add basic NVMEM cells dt-bindings: nvmem: make "reg" property optional nvmem: brcm_nvram: parse NVRAM content into NVMEM cells nvmem: dt-bindings: Fix the error of dt-bindings check ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig3
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/accessibility/speakup/speakup_audptr.c24
-rw-r--r--drivers/accessibility/speakup/synth.c2
-rw-r--r--drivers/android/binder_alloc.c8
-rw-r--r--drivers/ata/ahci.h2
-rw-r--r--drivers/ata/ahci_mvebu.c2
-rw-r--r--drivers/ata/libahci_platform.c2
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/mhi/Kconfig27
-rw-r--r--drivers/bus/mhi/Makefile8
-rw-r--r--drivers/bus/mhi/common.h304
-rw-r--r--drivers/bus/mhi/core/internal.h722
-rw-r--r--drivers/bus/mhi/host/Kconfig31
-rw-r--r--drivers/bus/mhi/host/Makefile (renamed from drivers/bus/mhi/core/Makefile)4
-rw-r--r--drivers/bus/mhi/host/boot.c (renamed from drivers/bus/mhi/core/boot.c)17
-rw-r--r--drivers/bus/mhi/host/debugfs.c (renamed from drivers/bus/mhi/core/debugfs.c)40
-rw-r--r--drivers/bus/mhi/host/init.c (renamed from drivers/bus/mhi/core/init.c)131
-rw-r--r--drivers/bus/mhi/host/internal.h382
-rw-r--r--drivers/bus/mhi/host/main.c (renamed from drivers/bus/mhi/core/main.c)66
-rw-r--r--drivers/bus/mhi/host/pci_generic.c (renamed from drivers/bus/mhi/pci_generic.c)1
-rw-r--r--drivers/bus/mhi/host/pm.c (renamed from drivers/bus/mhi/core/pm.c)36
-rw-r--r--drivers/char/bsr.c2
-rw-r--r--drivers/char/hpet.c28
-rw-r--r--drivers/char/virtio_console.c8
-rw-r--r--drivers/char/xilinx_hwicap/fifo_icap.c2
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c6
-rw-r--r--drivers/comedi/drivers/das16.c4
-rw-r--r--drivers/comedi/drivers/ni_routes.c6
-rw-r--r--drivers/comedi/drivers/pcm3724.c1
-rw-r--r--drivers/counter/Kconfig2
-rw-r--r--drivers/counter/counter-chrdev.c4
-rw-r--r--drivers/counter/counter-core.c12
-rw-r--r--drivers/counter/interrupt-cnt.c7
-rw-r--r--drivers/dio/dio.c140
-rw-r--r--drivers/firmware/google/Kconfig2
-rw-r--r--drivers/firmware/stratix10-svc.c11
-rw-r--r--drivers/firmware/sysfb_simplefb.c23
-rw-r--r--drivers/firmware/xilinx/zynqmp.c120
-rw-r--r--drivers/fpga/dfl-pci.c15
-rw-r--r--drivers/fsi/fsi-core.c11
-rw-r--r--drivers/fsi/fsi-master-aspeed.c19
-rw-r--r--drivers/fsi/fsi-occ.c87
-rw-r--r--drivers/fsi/fsi-sbefifo.c53
-rw-r--r--drivers/fsi/fsi-scom.c45
-rw-r--r--drivers/gpio/Kconfig12
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-tn48m.c100
-rw-r--r--drivers/greybus/svc.c16
-rw-r--r--drivers/hwmon/Kconfig2
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/peci/Kconfig31
-rw-r--r--drivers/hwmon/peci/Makefile7
-rw-r--r--drivers/hwmon/peci/common.h58
-rw-r--r--drivers/hwmon/peci/cputemp.c592
-rw-r--r--drivers/hwmon/peci/dimmtemp.c630
-rw-r--r--drivers/hwtracing/coresight/coresight-core.c3
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-core.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c12
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c38
-rw-r--r--drivers/hwtracing/coresight/coresight-platform.c8
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg.c2
-rw-r--r--drivers/hwtracing/coresight/coresight-trbe.c125
-rw-r--r--drivers/hwtracing/coresight/coresight-trbe.h8
-rw-r--r--drivers/iio/accel/Kconfig62
-rw-r--r--drivers/iio/accel/Makefile3
-rw-r--r--drivers/iio/accel/adis16201.c1
-rw-r--r--drivers/iio/accel/adis16209.c1
-rw-r--r--drivers/iio/accel/adxl313_core.c6
-rw-r--r--drivers/iio/accel/adxl313_i2c.c1
-rw-r--r--drivers/iio/accel/adxl313_spi.c1
-rw-r--r--drivers/iio/accel/adxl345.h7
-rw-r--r--drivers/iio/accel/adxl345_core.c56
-rw-r--r--drivers/iio/accel/adxl345_i2c.c35
-rw-r--r--drivers/iio/accel/adxl345_spi.c35
-rw-r--r--drivers/iio/accel/adxl355_core.c11
-rw-r--r--drivers/iio/accel/adxl355_i2c.c1
-rw-r--r--drivers/iio/accel/adxl355_spi.c1
-rw-r--r--drivers/iio/accel/adxl367.c1588
-rw-r--r--drivers/iio/accel/adxl367.h23
-rw-r--r--drivers/iio/accel/adxl367_i2c.c90
-rw-r--r--drivers/iio/accel/adxl367_spi.c164
-rw-r--r--drivers/iio/accel/adxl372.c4
-rw-r--r--drivers/iio/accel/adxl372_i2c.c1
-rw-r--r--drivers/iio/accel/adxl372_spi.c1
-rw-r--r--drivers/iio/accel/bma180.c9
-rw-r--r--drivers/iio/accel/bma400_core.c6
-rw-r--r--drivers/iio/accel/bma400_i2c.c1
-rw-r--r--drivers/iio/accel/bma400_spi.c1
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c8
-rw-r--r--drivers/iio/accel/bmc150-accel-i2c.c1
-rw-r--r--drivers/iio/accel/bmc150-accel-spi.c1
-rw-r--r--drivers/iio/accel/bmi088-accel-core.c8
-rw-r--r--drivers/iio/accel/bmi088-accel-spi.c1
-rw-r--r--drivers/iio/accel/da280.c6
-rw-r--r--drivers/iio/accel/da311.c6
-rw-r--r--drivers/iio/accel/dmard06.c10
-rw-r--r--drivers/iio/accel/dmard09.c2
-rw-r--r--drivers/iio/accel/dmard10.c7
-rw-r--r--drivers/iio/accel/fxls8962af-core.c8
-rw-r--r--drivers/iio/accel/fxls8962af-i2c.c1
-rw-r--r--drivers/iio/accel/fxls8962af-spi.c1
-rw-r--r--drivers/iio/accel/kxsd9-i2c.c1
-rw-r--r--drivers/iio/accel/kxsd9-spi.c1
-rw-r--r--drivers/iio/accel/kxsd9.c6
-rw-r--r--drivers/iio/accel/mc3230.c6
-rw-r--r--drivers/iio/accel/mma7455_core.c6
-rw-r--r--drivers/iio/accel/mma7455_i2c.c1
-rw-r--r--drivers/iio/accel/mma7455_spi.c1
-rw-r--r--drivers/iio/accel/mma7660.c11
-rw-r--r--drivers/iio/accel/mma8452.c54
-rw-r--r--drivers/iio/accel/mma9551.c12
-rw-r--r--drivers/iio/accel/mma9551_core.c36
-rw-r--r--drivers/iio/accel/mma9553.c12
-rw-r--r--drivers/iio/accel/ssp_accel_sensor.c1
-rw-r--r--drivers/iio/accel/st_accel.h2
-rw-r--r--drivers/iio/accel/st_accel_buffer.c5
-rw-r--r--drivers/iio/accel/st_accel_core.c88
-rw-r--r--drivers/iio/accel/st_accel_i2c.c6
-rw-r--r--drivers/iio/accel/st_accel_spi.c1
-rw-r--r--drivers/iio/accel/stk8312.c11
-rw-r--r--drivers/iio/accel/stk8ba50.c11
-rw-r--r--drivers/iio/adc/Kconfig11
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/ab8500-gpadc.c14
-rw-r--r--drivers/iio/adc/ad7091r-base.c4
-rw-r--r--drivers/iio/adc/ad7091r5.c1
-rw-r--r--drivers/iio/adc/ad7124.c1
-rw-r--r--drivers/iio/adc/ad7192.c5
-rw-r--r--drivers/iio/adc/ad7280a.c1111
-rw-r--r--drivers/iio/adc/ad7606.c4
-rw-r--r--drivers/iio/adc/ad7606_par.c1
-rw-r--r--drivers/iio/adc/ad7606_spi.c1
-rw-r--r--drivers/iio/adc/ad7780.c1
-rw-r--r--drivers/iio/adc/ad7791.c1
-rw-r--r--drivers/iio/adc/ad7793.c1
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c20
-rw-r--r--drivers/iio/adc/aspeed_adc.c4
-rw-r--r--drivers/iio/adc/at91_adc.c7
-rw-r--r--drivers/iio/adc/cpcap-adc.c2
-rw-r--r--drivers/iio/adc/exynos_adc.c9
-rw-r--r--drivers/iio/adc/hi8435.c2
-rw-r--r--drivers/iio/adc/ina2xx-adc.c2
-rw-r--r--drivers/iio/adc/max9611.c2
-rw-r--r--drivers/iio/adc/mt6577_auxadc.c16
-rw-r--r--drivers/iio/adc/palmas_gpadc.c10
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c15
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c24
-rw-r--r--drivers/iio/adc/qcom-vadc-common.c92
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c6
-rw-r--r--drivers/iio/adc/rn5t618-adc.c7
-rw-r--r--drivers/iio/adc/rockchip_saradc.c9
-rw-r--r--drivers/iio/adc/rzg2l_adc.c4
-rw-r--r--drivers/iio/adc/stm32-adc-core.c17
-rw-r--r--drivers/iio/adc/stm32-adc.c12
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c11
-rw-r--r--drivers/iio/adc/stm32-dfsdm-core.c19
-rw-r--r--drivers/iio/adc/ti-adc084s021.c2
-rw-r--r--drivers/iio/adc/ti-tsc2046.c269
-rw-r--r--drivers/iio/adc/twl4030-madc.c9
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c10
-rw-r--r--drivers/iio/adc/vf610_adc.c7
-rw-r--r--drivers/iio/adc/xilinx-ams.c26
-rw-r--r--drivers/iio/afe/iio-rescale.c288
-rw-r--r--drivers/iio/amplifiers/Kconfig11
-rw-r--r--drivers/iio/amplifiers/Makefile1
-rw-r--r--drivers/iio/amplifiers/ada4250.c403
-rw-r--r--drivers/iio/amplifiers/hmc425a.c6
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c2
-rw-r--r--drivers/iio/buffer/industrialio-hw-consumer.c4
-rw-r--r--drivers/iio/chemical/atlas-ezo-sensor.c32
-rw-r--r--drivers/iio/chemical/atlas-sensor.c17
-rw-r--r--drivers/iio/chemical/bme680_core.c4
-rw-r--r--drivers/iio/chemical/bme680_i2c.c1
-rw-r--r--drivers/iio/chemical/bme680_spi.c3
-rw-r--r--drivers/iio/chemical/scd4x.c2
-rw-r--r--drivers/iio/chemical/sps30.c2
-rw-r--r--drivers/iio/common/ms_sensors/ms_sensors_i2c.c28
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_dev.c40
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_iio.c7
-rw-r--r--drivers/iio/common/st_sensors/Kconfig2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_buffer.c7
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c28
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_i2c.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_spi.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c9
-rw-r--r--drivers/iio/dac/Kconfig11
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad5592r-base.c5
-rw-r--r--drivers/iio/dac/ad5592r.c1
-rw-r--r--drivers/iio/dac/ad5593r.c1
-rw-r--r--drivers/iio/dac/ad5686-spi.c1
-rw-r--r--drivers/iio/dac/ad5686.c4
-rw-r--r--drivers/iio/dac/ad5696-i2c.c1
-rw-r--r--drivers/iio/dac/ltc2688.c1071
-rw-r--r--drivers/iio/dac/m62332.c11
-rw-r--r--drivers/iio/dac/stm32-dac-core.c16
-rw-r--r--drivers/iio/dac/stm32-dac.c9
-rw-r--r--drivers/iio/dac/vf610_dac.c7
-rw-r--r--drivers/iio/frequency/Kconfig20
-rw-r--r--drivers/iio/frequency/Makefile2
-rw-r--r--drivers/iio/frequency/ad9523.c2
-rw-r--r--drivers/iio/frequency/adf4350.c103
-rw-r--r--drivers/iio/frequency/admv1013.c2
-rw-r--r--drivers/iio/frequency/admv1014.c823
-rw-r--r--drivers/iio/frequency/admv4420.c398
-rw-r--r--drivers/iio/gyro/Kconfig37
-rw-r--r--drivers/iio/gyro/adis16136.c1
-rw-r--r--drivers/iio/gyro/adis16260.c1
-rw-r--r--drivers/iio/gyro/ssp_gyro_sensor.c1
-rw-r--r--drivers/iio/gyro/st_gyro_buffer.c4
-rw-r--r--drivers/iio/gyro/st_gyro_core.c5
-rw-r--r--drivers/iio/gyro/st_gyro_i2c.c1
-rw-r--r--drivers/iio/gyro/st_gyro_spi.c1
-rw-r--r--drivers/iio/humidity/dht11.c3
-rw-r--r--drivers/iio/humidity/hdc100x.c7
-rw-r--r--drivers/iio/humidity/htu21.c1
-rw-r--r--drivers/iio/imu/adis.c67
-rw-r--r--drivers/iio/imu/adis16400.c1
-rw-r--r--drivers/iio/imu/adis16460.c1
-rw-r--r--drivers/iio/imu/adis16475.c1
-rw-r--r--drivers/iio/imu/adis16480.c1
-rw-r--r--drivers/iio/imu/adis_buffer.c10
-rw-r--r--drivers/iio/imu/adis_trigger.c5
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c5
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c15
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c5
-rw-r--r--drivers/iio/imu/kmx61.c10
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c4
-rw-r--r--drivers/iio/imu/st_lsm9ds0/Kconfig28
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c3
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c1
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c1
-rw-r--r--drivers/iio/industrialio-buffer.c4
-rw-r--r--drivers/iio/industrialio-core.c2
-rw-r--r--drivers/iio/industrialio-event.c1
-rw-r--r--drivers/iio/inkern.c40
-rw-r--r--drivers/iio/light/apds9300.c10
-rw-r--r--drivers/iio/light/bh1780.c12
-rw-r--r--drivers/iio/light/cm3232.c9
-rw-r--r--drivers/iio/light/isl29018.c10
-rw-r--r--drivers/iio/light/isl29125.c7
-rw-r--r--drivers/iio/light/jsa1212.c11
-rw-r--r--drivers/iio/light/lm3533-als.c6
-rw-r--r--drivers/iio/light/ltr501.c20
-rw-r--r--drivers/iio/light/pa12203001.c4
-rw-r--r--drivers/iio/light/rpr0521.c7
-rw-r--r--drivers/iio/light/st_uvis25_core.c4
-rw-r--r--drivers/iio/light/st_uvis25_i2c.c1
-rw-r--r--drivers/iio/light/st_uvis25_spi.c1
-rw-r--r--drivers/iio/light/stk3310.c11
-rw-r--r--drivers/iio/light/tcs3414.c7
-rw-r--r--drivers/iio/light/tcs3472.c7
-rw-r--r--drivers/iio/light/tsl2563.c10
-rw-r--r--drivers/iio/light/tsl2772.c2
-rw-r--r--drivers/iio/light/tsl4531.c10
-rw-r--r--drivers/iio/light/us5182d.c6
-rw-r--r--drivers/iio/light/vcnl4035.c2
-rw-r--r--drivers/iio/magnetometer/Kconfig35
-rw-r--r--drivers/iio/magnetometer/ak8975.c12
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c8
-rw-r--r--drivers/iio/magnetometer/bmc150_magn_i2c.c1
-rw-r--r--drivers/iio/magnetometer/bmc150_magn_spi.c1
-rw-r--r--drivers/iio/magnetometer/hmc5843_core.c8
-rw-r--r--drivers/iio/magnetometer/hmc5843_i2c.c1
-rw-r--r--drivers/iio/magnetometer/hmc5843_spi.c1
-rw-r--r--drivers/iio/magnetometer/mag3110.c10
-rw-r--r--drivers/iio/magnetometer/mmc35240.c9
-rw-r--r--drivers/iio/magnetometer/rm3100-core.c8
-rw-r--r--drivers/iio/magnetometer/rm3100-i2c.c1
-rw-r--r--drivers/iio/magnetometer/rm3100-spi.c1
-rw-r--r--drivers/iio/magnetometer/st_magn_buffer.c4
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c5
-rw-r--r--drivers/iio/magnetometer/st_magn_i2c.c1
-rw-r--r--drivers/iio/magnetometer/st_magn_spi.c1
-rw-r--r--drivers/iio/potentiometer/Kconfig6
-rw-r--r--drivers/iio/potentiometer/ds1803.c169
-rw-r--r--drivers/iio/pressure/Kconfig35
-rw-r--r--drivers/iio/pressure/dps310.c7
-rw-r--r--drivers/iio/pressure/mpl115.c2
-rw-r--r--drivers/iio/pressure/mpl115_i2c.c1
-rw-r--r--drivers/iio/pressure/mpl115_spi.c1
-rw-r--r--drivers/iio/pressure/mpl3115.c10
-rw-r--r--drivers/iio/pressure/ms5611_core.c4
-rw-r--r--drivers/iio/pressure/ms5611_i2c.c1
-rw-r--r--drivers/iio/pressure/ms5611_spi.c1
-rw-r--r--drivers/iio/pressure/ms5637.c1
-rw-r--r--drivers/iio/pressure/st_pressure_buffer.c5
-rw-r--r--drivers/iio/pressure/st_pressure_core.c5
-rw-r--r--drivers/iio/pressure/st_pressure_i2c.c1
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c1
-rw-r--r--drivers/iio/pressure/zpa2326.c12
-rw-r--r--drivers/iio/pressure/zpa2326_i2c.c1
-rw-r--r--drivers/iio/pressure/zpa2326_spi.c1
-rw-r--r--drivers/iio/proximity/Kconfig34
-rw-r--r--drivers/iio/proximity/Makefile3
-rw-r--r--drivers/iio/proximity/as3935.c26
-rw-r--r--drivers/iio/proximity/ping.c4
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c7
-rw-r--r--drivers/iio/proximity/rfd77402.c9
-rw-r--r--drivers/iio/proximity/srf04.c12
-rw-r--r--drivers/iio/proximity/srf08.c6
-rw-r--r--drivers/iio/proximity/sx9310.c741
-rw-r--r--drivers/iio/proximity/sx9324.c1068
-rw-r--r--drivers/iio/proximity/sx9360.c893
-rw-r--r--drivers/iio/proximity/sx9500.c8
-rw-r--r--drivers/iio/proximity/sx_common.c572
-rw-r--r--drivers/iio/proximity/sx_common.h157
-rw-r--r--drivers/iio/proximity/vl53l0x-i2c.c2
-rw-r--r--drivers/iio/temperature/max31856.c4
-rw-r--r--drivers/iio/temperature/max31865.c4
-rw-r--r--drivers/iio/temperature/maxim_thermocouple.c5
-rw-r--r--drivers/iio/temperature/mlx90614.c12
-rw-r--r--drivers/iio/temperature/mlx90632.c2
-rw-r--r--drivers/iio/temperature/tmp006.c6
-rw-r--r--drivers/iio/temperature/tmp007.c6
-rw-r--r--drivers/iio/temperature/tsys01.c1
-rw-r--r--drivers/iio/temperature/tsys02d.c1
-rw-r--r--drivers/iio/test/Kconfig10
-rw-r--r--drivers/iio/test/Makefile1
-rw-r--r--drivers/iio/test/iio-test-rescale.c710
-rw-r--r--drivers/iio/trigger/Kconfig2
-rw-r--r--drivers/iio/trigger/stm32-timer-trigger.c23
-rw-r--r--drivers/interconnect/imx/imx.c9
-rw-r--r--drivers/interconnect/qcom/msm8939.c10
-rw-r--r--drivers/misc/Kconfig13
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_dev.c1
-rw-r--r--drivers/misc/cardreader/alcor_pci.c9
-rw-r--r--drivers/misc/cardreader/rtl8411.c2
-rw-r--r--drivers/misc/cardreader/rts5209.c2
-rw-r--r--drivers/misc/cardreader/rts5227.c47
-rw-r--r--drivers/misc/cardreader/rts5228.c25
-rw-r--r--drivers/misc/cardreader/rts5229.c2
-rw-r--r--drivers/misc/cardreader/rts5249.c31
-rw-r--r--drivers/misc/cardreader/rts5261.c35
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.c228
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.h3
-rw-r--r--drivers/misc/eeprom/at25.c4
-rw-r--r--drivers/misc/fastrpc.c556
-rw-r--r--drivers/misc/habanalabs/common/Makefile2
-rw-r--r--drivers/misc/habanalabs/common/command_buffer.c4
-rw-r--r--drivers/misc/habanalabs/common/command_submission.c265
-rw-r--r--drivers/misc/habanalabs/common/debugfs.c40
-rw-r--r--drivers/misc/habanalabs/common/device.c53
-rw-r--r--drivers/misc/habanalabs/common/firmware_if.c152
-rw-r--r--drivers/misc/habanalabs/common/habanalabs.h209
-rw-r--r--drivers/misc/habanalabs/common/habanalabs_drv.c3
-rw-r--r--drivers/misc/habanalabs/common/habanalabs_ioctl.c13
-rw-r--r--drivers/misc/habanalabs/common/hwmgr.c117
-rw-r--r--drivers/misc/habanalabs/common/irq.c127
-rw-r--r--drivers/misc/habanalabs/common/memory.c360
-rw-r--r--drivers/misc/habanalabs/common/mmu/mmu.c55
-rw-r--r--drivers/misc/habanalabs/common/mmu/mmu_v1.c47
-rw-r--r--drivers/misc/habanalabs/common/pci/pci.c9
-rw-r--r--drivers/misc/habanalabs/common/sysfs.c176
-rw-r--r--drivers/misc/habanalabs/gaudi/gaudi.c462
-rw-r--r--drivers/misc/habanalabs/gaudi/gaudiP.h8
-rw-r--r--drivers/misc/habanalabs/goya/goya.c45
-rw-r--r--drivers/misc/habanalabs/goya/goyaP.h6
-rw-r--r--drivers/misc/habanalabs/goya/goya_hwmgr.c67
-rw-r--r--drivers/misc/habanalabs/include/common/cpucp_if.h2
-rw-r--r--drivers/misc/habanalabs/include/common/hl_boot_if.h5
-rw-r--r--drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h10
-rw-r--r--drivers/misc/kgdbts.c4
-rw-r--r--drivers/misc/lkdtm/fortify.c6
-rw-r--r--drivers/misc/mei/client.c1
-rw-r--r--drivers/misc/mei/hw-me-regs.h2
-rw-r--r--drivers/misc/mei/hw-me.c43
-rw-r--r--drivers/misc/mei/init.c5
-rw-r--r--drivers/misc/mei/interrupt.c35
-rw-r--r--drivers/misc/mei/pci-me.c11
-rw-r--r--drivers/misc/ocxl/link.c2
-rw-r--r--drivers/misc/open-dice.c208
-rw-r--r--drivers/misc/sgi-gru/grukservices.c2
-rw-r--r--drivers/misc/sgi-gru/grutables.h6
-rw-r--r--drivers/misc/vmw_vmci/vmci_guest.c366
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c20
-rw-r--r--drivers/mtd/mtdcore.c13
-rw-r--r--drivers/mux/core.c239
-rw-r--r--drivers/nvmem/Kconfig24
-rw-r--r--drivers/nvmem/Makefile4
-rw-r--r--drivers/nvmem/brcm_nvram.c90
-rw-r--r--drivers/nvmem/core.c47
-rw-r--r--drivers/nvmem/layerscape-sfp.c89
-rw-r--r--drivers/nvmem/meson-mx-efuse.c3
-rw-r--r--drivers/nvmem/qfprom.c4
-rw-r--r--drivers/nvmem/sunplus-ocotp.c228
-rw-r--r--drivers/nvmem/sunxi_sid.c6
-rw-r--r--drivers/of/platform.c1
-rw-r--r--drivers/parport/Kconfig4
-rw-r--r--drivers/pci/controller/pci-aardvark.c4
-rw-r--r--drivers/peci/Kconfig36
-rw-r--r--drivers/peci/Makefile10
-rw-r--r--drivers/peci/controller/Kconfig18
-rw-r--r--drivers/peci/controller/Makefile3
-rw-r--r--drivers/peci/controller/peci-aspeed.c599
-rw-r--r--drivers/peci/core.c236
-rw-r--r--drivers/peci/cpu.c343
-rw-r--r--drivers/peci/device.c252
-rw-r--r--drivers/peci/internal.h136
-rw-r--r--drivers/peci/request.c482
-rw-r--r--drivers/peci/sysfs.c82
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c41
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb2.c5
-rw-r--r--drivers/phy/amlogic/phy-meson8b-usb2.c9
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c46
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.c36
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.h1
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb.c11
-rw-r--r--drivers/phy/cadence/Kconfig8
-rw-r--r--drivers/phy/cadence/Makefile1
-rw-r--r--drivers/phy/cadence/cdns-dphy-rx.c255
-rw-r--r--drivers/phy/cadence/phy-cadence-salvo.c7
-rw-r--r--drivers/phy/cadence/phy-cadence-sierra.c82
-rw-r--r--drivers/phy/freescale/Kconfig5
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c3
-rw-r--r--drivers/phy/marvell/phy-mvebu-a3700-comphy.c1350
-rw-r--r--drivers/phy/phy-core-mipi-dphy.c4
-rw-r--r--drivers/phy/qualcomm/phy-qcom-edp.c9
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c28
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.c3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c5
-rw-r--r--drivers/phy/rockchip/Kconfig8
-rw-r--r--drivers/phy/rockchip/Makefile1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c581
-rw-r--r--drivers/phy/ti/phy-tusb1210.c443
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.c8
-rw-r--r--drivers/pps/clients/pps-gpio.c2
-rw-r--r--drivers/pps/generators/pps_gen_parport.c42
-rw-r--r--drivers/reset/Kconfig13
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-tn48m.c128
-rw-r--r--drivers/soundwire/bus.c8
-rw-r--r--drivers/soundwire/intel.c4
-rw-r--r--drivers/soundwire/intel_init.c7
-rw-r--r--drivers/soundwire/qcom.c208
-rw-r--r--drivers/soundwire/stream.c942
-rw-r--r--drivers/staging/iio/accel/adis16203.c1
-rw-r--r--drivers/staging/iio/accel/adis16240.c1
-rw-r--r--drivers/staging/iio/adc/Kconfig11
-rw-r--r--drivers/staging/iio/adc/Makefile1
-rw-r--r--drivers/staging/iio/adc/ad7280a.c1044
-rw-r--r--drivers/staging/iio/adc/ad7280a.h37
-rw-r--r--drivers/thunderbolt/nvm.c6
-rw-r--r--drivers/usb/host/xhci-mvebu.c42
-rw-r--r--drivers/usb/host/xhci-mvebu.h6
-rw-r--r--drivers/usb/host/xhci-plat.c20
-rw-r--r--drivers/usb/host/xhci-plat.h1
-rw-r--r--drivers/virt/acrn/hsm.c20
-rw-r--r--drivers/virt/acrn/irqfd.c1
-rw-r--r--drivers/virt/acrn/mm.c24
-rw-r--r--drivers/virt/fsl_hypervisor.c4
-rw-r--r--drivers/w1/masters/ds2490.c8
-rw-r--r--drivers/w1/slaves/w1_therm.c78
456 files changed, 23167 insertions, 6123 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 0d399ddaa185..8d6cd5d08722 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -236,4 +236,7 @@ source "drivers/interconnect/Kconfig"
source "drivers/counter/Kconfig"
source "drivers/most/Kconfig"
+
+source "drivers/peci/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index a110338c860c..020780b6b4d2 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -187,3 +187,4 @@ obj-$(CONFIG_GNSS) += gnss/
obj-$(CONFIG_INTERCONNECT) += interconnect/
obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
+obj-$(CONFIG_PECI) += peci/
diff --git a/drivers/accessibility/speakup/speakup_audptr.c b/drivers/accessibility/speakup/speakup_audptr.c
index e89fd72579e6..a0c3b8ae17a1 100644
--- a/drivers/accessibility/speakup/speakup_audptr.c
+++ b/drivers/accessibility/speakup/speakup_audptr.c
@@ -126,20 +126,22 @@ static void synth_flush(struct spk_synth *synth)
static void synth_version(struct spk_synth *synth)
{
- unsigned char test = 0;
- char synth_id[40] = "";
+ unsigned i;
+ char synth_id[33];
synth->synth_immediate(synth, "\x05[Q]");
- synth_id[test] = synth->io_ops->synth_in(synth);
- if (synth_id[test] == 'A') {
- do {
- /* read version string from synth */
- synth_id[++test] = synth->io_ops->synth_in(synth);
- } while (synth_id[test] != '\n' && test < 32);
- synth_id[++test] = 0x00;
+ synth_id[0] = synth->io_ops->synth_in(synth);
+ if (synth_id[0] != 'A')
+ return;
+
+ for (i = 1; i < sizeof(synth_id) - 1; i++) {
+ /* read version string from synth */
+ synth_id[i] = synth->io_ops->synth_in(synth);
+ if (synth_id[i] == '\n')
+ break;
}
- if (synth_id[0] == 'A')
- pr_info("%s version: %s", synth->long_name, synth_id);
+ synth_id[i] = '\0';
+ pr_info("%s version: %s", synth->long_name, synth_id);
}
static int synth_probe(struct spk_synth *synth)
diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c
index 2b8699673bac..eea2a2fa4f01 100644
--- a/drivers/accessibility/speakup/synth.c
+++ b/drivers/accessibility/speakup/synth.c
@@ -348,7 +348,7 @@ struct var_t synth_time_vars[] = {
{ TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } },
{ JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } },
{ FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } },
- { FLUSH, .u.n = {NULL, 4000, 100, 4000, 0, 0, NULL } },
+ { FLUSH, .u.n = {NULL, 4000, 10, 4000, 0, 0, NULL } },
V_LAST_VAR
};
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 47bc74a8c7b6..2ac1008a5f39 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -1049,18 +1049,14 @@ err_get_alloc_mutex_failed:
static unsigned long
binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
{
- unsigned long ret = list_lru_count(&binder_alloc_lru);
- return ret;
+ return list_lru_count(&binder_alloc_lru);
}
static unsigned long
binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
{
- unsigned long ret;
-
- ret = list_lru_walk(&binder_alloc_lru, binder_alloc_free_page,
+ return list_lru_walk(&binder_alloc_lru, binder_alloc_free_page,
NULL, sc->nr_to_scan);
- return ret;
}
static struct shrinker binder_shrinker = {
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 5badbaca05a0..6ead58c1b6e5 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -240,8 +240,6 @@ enum {
as default lpm_policy */
AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during
suspend/resume */
- AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = (1 << 27), /* ignore -EOPNOTSUPP
- from phy_power_on() */
AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */
/* ap->flags bits */
diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c
index 991413a272e6..22ecc4f3ae79 100644
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data ahci_mvebu_armada_380_plat_data = {
static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
.plat_config = ahci_mvebu_armada_3700_config,
- .flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON,
+ .flags = AHCI_HFLAG_SUSPEND_PHYS,
};
static const struct of_device_id ahci_mvebu_of_match[] = {
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index 65227ef6b846..32495ae96567 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
}
rc = phy_power_on(hpriv->phys[i]);
- if (rc && !(rc == -EOPNOTSUPP && (hpriv->flags & AHCI_HFLAG_IGN_NOTSUPP_POWER_ON))) {
+ if (rc) {
phy_exit(hpriv->phys[i]);
goto disable_phys;
}
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 52c2f35a26a9..16da51130d1a 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -39,4 +39,4 @@ obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o
# MHI
-obj-$(CONFIG_MHI_BUS) += mhi/
+obj-y += mhi/
diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig
index da5cd0c9fc62..4748df7f9cd5 100644
--- a/drivers/bus/mhi/Kconfig
+++ b/drivers/bus/mhi/Kconfig
@@ -2,30 +2,7 @@
#
# MHI bus
#
-# Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+# Copyright (c) 2021, Linaro Ltd.
#
-config MHI_BUS
- tristate "Modem Host Interface (MHI) bus"
- help
- Bus driver for MHI protocol. Modem Host Interface (MHI) is a
- communication protocol used by the host processors to control
- and communicate with modem devices over a high speed peripheral
- bus or shared memory.
-
-config MHI_BUS_DEBUG
- bool "Debugfs support for the MHI bus"
- depends on MHI_BUS && DEBUG_FS
- help
- Enable debugfs support for use with the MHI transport. Allows
- reading and/or modifying some values within the MHI controller
- for debug and test purposes.
-
-config MHI_BUS_PCI_GENERIC
- tristate "MHI PCI controller driver"
- depends on MHI_BUS
- depends on PCI
- help
- This driver provides MHI PCI controller driver for devices such as
- Qualcomm SDX55 based PCIe modems.
-
+source "drivers/bus/mhi/host/Kconfig"
diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
index 0a2d778d6fb4..5f5708a249f5 100644
--- a/drivers/bus/mhi/Makefile
+++ b/drivers/bus/mhi/Makefile
@@ -1,6 +1,2 @@
-# core layer
-obj-y += core/
-
-obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
-mhi_pci_generic-y += pci_generic.o
-
+# Host MHI stack
+obj-y += host/
diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
new file mode 100644
index 000000000000..b4ef9acd3ce7
--- /dev/null
+++ b/drivers/bus/mhi/common.h
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022, Linaro Ltd.
+ *
+ */
+
+#ifndef _MHI_COMMON_H
+#define _MHI_COMMON_H
+
+#include <linux/bitfield.h>
+#include <linux/mhi.h>
+
+/* MHI registers */
+#define MHIREGLEN 0x00
+#define MHIVER 0x08
+#define MHICFG 0x10
+#define CHDBOFF 0x18
+#define ERDBOFF 0x20
+#define BHIOFF 0x28
+#define BHIEOFF 0x2c
+#define DEBUGOFF 0x30
+#define MHICTRL 0x38
+#define MHISTATUS 0x48
+#define CCABAP_LOWER 0x58
+#define CCABAP_HIGHER 0x5c
+#define ECABAP_LOWER 0x60
+#define ECABAP_HIGHER 0x64
+#define CRCBAP_LOWER 0x68
+#define CRCBAP_HIGHER 0x6c
+#define CRDB_LOWER 0x70
+#define CRDB_HIGHER 0x74
+#define MHICTRLBASE_LOWER 0x80
+#define MHICTRLBASE_HIGHER 0x84
+#define MHICTRLLIMIT_LOWER 0x88
+#define MHICTRLLIMIT_HIGHER 0x8c
+#define MHIDATABASE_LOWER 0x98
+#define MHIDATABASE_HIGHER 0x9c
+#define MHIDATALIMIT_LOWER 0xa0
+#define MHIDATALIMIT_HIGHER 0xa4
+
+/* MHI BHI registers */
+#define BHI_BHIVERSION_MINOR 0x00
+#define BHI_BHIVERSION_MAJOR 0x04
+#define BHI_IMGADDR_LOW 0x08
+#define BHI_IMGADDR_HIGH 0x0c
+#define BHI_IMGSIZE 0x10
+#define BHI_RSVD1 0x14
+#define BHI_IMGTXDB 0x18
+#define BHI_RSVD2 0x1c
+#define BHI_INTVEC 0x20
+#define BHI_RSVD3 0x24
+#define BHI_EXECENV 0x28
+#define BHI_STATUS 0x2c
+#define BHI_ERRCODE 0x30
+#define BHI_ERRDBG1 0x34
+#define BHI_ERRDBG2 0x38
+#define BHI_ERRDBG3 0x3c
+#define BHI_SERIALNU 0x40
+#define BHI_SBLANTIROLLVER 0x44
+#define BHI_NUMSEG 0x48
+#define BHI_MSMHWID(n) (0x4c + (0x4 * (n)))
+#define BHI_OEMPKHASH(n) (0x64 + (0x4 * (n)))
+#define BHI_RSVD5 0xc4
+
+/* BHI register bits */
+#define BHI_TXDB_SEQNUM_BMSK GENMASK(29, 0)
+#define BHI_TXDB_SEQNUM_SHFT 0
+#define BHI_STATUS_MASK GENMASK(31, 30)
+#define BHI_STATUS_ERROR 0x03
+#define BHI_STATUS_SUCCESS 0x02
+#define BHI_STATUS_RESET 0x00
+
+/* MHI BHIE registers */
+#define BHIE_MSMSOCID_OFFS 0x00
+#define BHIE_TXVECADDR_LOW_OFFS 0x2c
+#define BHIE_TXVECADDR_HIGH_OFFS 0x30
+#define BHIE_TXVECSIZE_OFFS 0x34
+#define BHIE_TXVECDB_OFFS 0x3c
+#define BHIE_TXVECSTATUS_OFFS 0x44
+#define BHIE_RXVECADDR_LOW_OFFS 0x60
+#define BHIE_RXVECADDR_HIGH_OFFS 0x64
+#define BHIE_RXVECSIZE_OFFS 0x68
+#define BHIE_RXVECDB_OFFS 0x70
+#define BHIE_RXVECSTATUS_OFFS 0x78
+
+/* BHIE register bits */
+#define BHIE_TXVECDB_SEQNUM_BMSK GENMASK(29, 0)
+#define BHIE_TXVECDB_SEQNUM_SHFT 0
+#define BHIE_TXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0)
+#define BHIE_TXVECSTATUS_SEQNUM_SHFT 0
+#define BHIE_TXVECSTATUS_STATUS_BMSK GENMASK(31, 30)
+#define BHIE_TXVECSTATUS_STATUS_SHFT 30
+#define BHIE_TXVECSTATUS_STATUS_RESET 0x00
+#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL 0x02
+#define BHIE_TXVECSTATUS_STATUS_ERROR 0x03
+#define BHIE_RXVECDB_SEQNUM_BMSK GENMASK(29, 0)
+#define BHIE_RXVECDB_SEQNUM_SHFT 0
+#define BHIE_RXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0)
+#define BHIE_RXVECSTATUS_SEQNUM_SHFT 0
+#define BHIE_RXVECSTATUS_STATUS_BMSK GENMASK(31, 30)
+#define BHIE_RXVECSTATUS_STATUS_SHFT 30
+#define BHIE_RXVECSTATUS_STATUS_RESET 0x00
+#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL 0x02
+#define BHIE_RXVECSTATUS_STATUS_ERROR 0x03
+
+/* MHI register bits */
+#define MHICFG_NHWER_MASK GENMASK(31, 24)
+#define MHICFG_NER_MASK GENMASK(23, 16)
+#define MHICFG_NHWCH_MASK GENMASK(15, 8)
+#define MHICFG_NCH_MASK GENMASK(7, 0)
+#define MHICTRL_MHISTATE_MASK GENMASK(15, 8)
+#define MHICTRL_RESET_MASK BIT(1)
+#define MHISTATUS_MHISTATE_MASK GENMASK(15, 8)
+#define MHISTATUS_SYSERR_MASK BIT(2)
+#define MHISTATUS_READY_MASK BIT(0)
+
+/* Command Ring Element macros */
+/* No operation command */
+#define MHI_TRE_CMD_NOOP_PTR 0
+#define MHI_TRE_CMD_NOOP_DWORD0 0
+#define MHI_TRE_CMD_NOOP_DWORD1 cpu_to_le32(FIELD_PREP(GENMASK(23, 16), MHI_CMD_NOP))
+
+/* Channel reset command */
+#define MHI_TRE_CMD_RESET_PTR 0
+#define MHI_TRE_CMD_RESET_DWORD0 0
+#define MHI_TRE_CMD_RESET_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
+ FIELD_PREP(GENMASK(23, 16), \
+ MHI_CMD_RESET_CHAN))
+
+/* Channel stop command */
+#define MHI_TRE_CMD_STOP_PTR 0
+#define MHI_TRE_CMD_STOP_DWORD0 0
+#define MHI_TRE_CMD_STOP_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
+ FIELD_PREP(GENMASK(23, 16), \
+ MHI_CMD_STOP_CHAN))
+
+/* Channel start command */
+#define MHI_TRE_CMD_START_PTR 0
+#define MHI_TRE_CMD_START_DWORD0 0
+#define MHI_TRE_CMD_START_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
+ FIELD_PREP(GENMASK(23, 16), \
+ MHI_CMD_START_CHAN))
+
+#define MHI_TRE_GET_DWORD(tre, word) le32_to_cpu((tre)->dword[(word)])
+#define MHI_TRE_GET_CMD_CHID(tre) FIELD_GET(GENMASK(31, 24), MHI_TRE_GET_DWORD(tre, 1))
+#define MHI_TRE_GET_CMD_TYPE(tre) FIELD_GET(GENMASK(23, 16), MHI_TRE_GET_DWORD(tre, 1))
+
+/* Event descriptor macros */
+#define MHI_TRE_EV_PTR(ptr) cpu_to_le64(ptr)
+#define MHI_TRE_EV_DWORD0(code, len) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), code) | \
+ FIELD_PREP(GENMASK(15, 0), len))
+#define MHI_TRE_EV_DWORD1(chid, type) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
+ FIELD_PREP(GENMASK(23, 16), type))
+#define MHI_TRE_GET_EV_PTR(tre) le64_to_cpu((tre)->ptr)
+#define MHI_TRE_GET_EV_CODE(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0)))
+#define MHI_TRE_GET_EV_LEN(tre) FIELD_GET(GENMASK(15, 0), (MHI_TRE_GET_DWORD(tre, 0)))
+#define MHI_TRE_GET_EV_CHID(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 1)))
+#define MHI_TRE_GET_EV_TYPE(tre) FIELD_GET(GENMASK(23, 16), (MHI_TRE_GET_DWORD(tre, 1)))
+#define MHI_TRE_GET_EV_STATE(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0)))
+#define MHI_TRE_GET_EV_EXECENV(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0)))
+#define MHI_TRE_GET_EV_SEQ(tre) MHI_TRE_GET_DWORD(tre, 0)
+#define MHI_TRE_GET_EV_TIME(tre) MHI_TRE_GET_EV_PTR(tre)
+#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits(MHI_TRE_GET_EV_PTR(tre))
+#define MHI_TRE_GET_EV_VEID(tre) FIELD_GET(GENMASK(23, 16), (MHI_TRE_GET_DWORD(tre, 0)))
+#define MHI_TRE_GET_EV_LINKSPEED(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 1)))
+#define MHI_TRE_GET_EV_LINKWIDTH(tre) FIELD_GET(GENMASK(7, 0), (MHI_TRE_GET_DWORD(tre, 0)))
+
+/* Transfer descriptor macros */
+#define MHI_TRE_DATA_PTR(ptr) cpu_to_le64(ptr)
+#define MHI_TRE_DATA_DWORD0(len) cpu_to_le32(FIELD_PREP(GENMASK(15, 0), len))
+#define MHI_TRE_TYPE_TRANSFER 2
+#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) cpu_to_le32(FIELD_PREP(GENMASK(23, 16), \
+ MHI_TRE_TYPE_TRANSFER) | \
+ FIELD_PREP(BIT(10), bei) | \
+ FIELD_PREP(BIT(9), ieot) | \
+ FIELD_PREP(BIT(8), ieob) | \
+ FIELD_PREP(BIT(0), chain))
+
+/* RSC transfer descriptor macros */
+#define MHI_RSCTRE_DATA_PTR(ptr, len) cpu_to_le64(FIELD_PREP(GENMASK(64, 48), len) | ptr)
+#define MHI_RSCTRE_DATA_DWORD0(cookie) cpu_to_le32(cookie)
+#define MHI_RSCTRE_DATA_DWORD1 cpu_to_le32(FIELD_PREP(GENMASK(23, 16), \
+ MHI_PKT_TYPE_COALESCING))
+
+enum mhi_pkt_type {
+ MHI_PKT_TYPE_INVALID = 0x0,
+ MHI_PKT_TYPE_NOOP_CMD = 0x1,
+ MHI_PKT_TYPE_TRANSFER = 0x2,
+ MHI_PKT_TYPE_COALESCING = 0x8,
+ MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10,
+ MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11,
+ MHI_PKT_TYPE_START_CHAN_CMD = 0x12,
+ MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20,
+ MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21,
+ MHI_PKT_TYPE_TX_EVENT = 0x22,
+ MHI_PKT_TYPE_RSC_TX_EVENT = 0x28,
+ MHI_PKT_TYPE_EE_EVENT = 0x40,
+ MHI_PKT_TYPE_TSYNC_EVENT = 0x48,
+ MHI_PKT_TYPE_BW_REQ_EVENT = 0x50,
+ MHI_PKT_TYPE_STALE_EVENT, /* internal event */
+};
+
+/* MHI transfer completion events */
+enum mhi_ev_ccs {
+ MHI_EV_CC_INVALID = 0x0,
+ MHI_EV_CC_SUCCESS = 0x1,
+ MHI_EV_CC_EOT = 0x2, /* End of transfer event */
+ MHI_EV_CC_OVERFLOW = 0x3,
+ MHI_EV_CC_EOB = 0x4, /* End of block event */
+ MHI_EV_CC_OOB = 0x5, /* Out of block event */
+ MHI_EV_CC_DB_MODE = 0x6,
+ MHI_EV_CC_UNDEFINED_ERR = 0x10,
+ MHI_EV_CC_BAD_TRE = 0x11,
+};
+
+/* Channel state */
+enum mhi_ch_state {
+ MHI_CH_STATE_DISABLED,
+ MHI_CH_STATE_ENABLED,
+ MHI_CH_STATE_RUNNING,
+ MHI_CH_STATE_SUSPENDED,
+ MHI_CH_STATE_STOP,
+ MHI_CH_STATE_ERROR,
+};
+
+enum mhi_cmd_type {
+ MHI_CMD_NOP = 1,
+ MHI_CMD_RESET_CHAN = 16,
+ MHI_CMD_STOP_CHAN = 17,
+ MHI_CMD_START_CHAN = 18,
+};
+
+#define EV_CTX_RESERVED_MASK GENMASK(7, 0)
+#define EV_CTX_INTMODC_MASK GENMASK(15, 8)
+#define EV_CTX_INTMODT_MASK GENMASK(31, 16)
+struct mhi_event_ctxt {
+ __le32 intmod;
+ __le32 ertype;
+ __le32 msivec;
+
+ __le64 rbase __packed __aligned(4);
+ __le64 rlen __packed __aligned(4);
+ __le64 rp __packed __aligned(4);
+ __le64 wp __packed __aligned(4);
+};
+
+#define CHAN_CTX_CHSTATE_MASK GENMASK(7, 0)
+#define CHAN_CTX_BRSTMODE_MASK GENMASK(9, 8)
+#define CHAN_CTX_POLLCFG_MASK GENMASK(15, 10)
+#define CHAN_CTX_RESERVED_MASK GENMASK(31, 16)
+struct mhi_chan_ctxt {
+ __le32 chcfg;
+ __le32 chtype;
+ __le32 erindex;
+
+ __le64 rbase __packed __aligned(4);
+ __le64 rlen __packed __aligned(4);
+ __le64 rp __packed __aligned(4);
+ __le64 wp __packed __aligned(4);
+};
+
+struct mhi_cmd_ctxt {
+ __le32 reserved0;
+ __le32 reserved1;
+ __le32 reserved2;
+
+ __le64 rbase __packed __aligned(4);
+ __le64 rlen __packed __aligned(4);
+ __le64 rp __packed __aligned(4);
+ __le64 wp __packed __aligned(4);
+};
+
+struct mhi_ring_element {
+ __le64 ptr;
+ __le32 dword[2];
+};
+
+static inline const char *mhi_state_str(enum mhi_state state)
+{
+ switch (state) {
+ case MHI_STATE_RESET:
+ return "RESET";
+ case MHI_STATE_READY:
+ return "READY";
+ case MHI_STATE_M0:
+ return "M0";
+ case MHI_STATE_M1:
+ return "M1";
+ case MHI_STATE_M2:
+ return "M2";
+ case MHI_STATE_M3:
+ return "M3";
+ case MHI_STATE_M3_FAST:
+ return "M3 FAST";
+ case MHI_STATE_BHI:
+ return "BHI";
+ case MHI_STATE_SYS_ERR:
+ return "SYS ERROR";
+ default:
+ return "Unknown state";
+ }
+};
+
+#endif /* _MHI_COMMON_H */
diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
deleted file mode 100644
index e2e10474a9d9..000000000000
--- a/drivers/bus/mhi/core/internal.h
+++ /dev/null
@@ -1,722 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
- *
- */
-
-#ifndef _MHI_INT_H
-#define _MHI_INT_H
-
-#include <linux/mhi.h>
-
-extern struct bus_type mhi_bus_type;
-
-#define MHIREGLEN (0x0)
-#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF)
-#define MHIREGLEN_MHIREGLEN_SHIFT (0)
-
-#define MHIVER (0x8)
-#define MHIVER_MHIVER_MASK (0xFFFFFFFF)
-#define MHIVER_MHIVER_SHIFT (0)
-
-#define MHICFG (0x10)
-#define MHICFG_NHWER_MASK (0xFF000000)
-#define MHICFG_NHWER_SHIFT (24)
-#define MHICFG_NER_MASK (0xFF0000)
-#define MHICFG_NER_SHIFT (16)
-#define MHICFG_NHWCH_MASK (0xFF00)
-#define MHICFG_NHWCH_SHIFT (8)
-#define MHICFG_NCH_MASK (0xFF)
-#define MHICFG_NCH_SHIFT (0)
-
-#define CHDBOFF (0x18)
-#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF)
-#define CHDBOFF_CHDBOFF_SHIFT (0)
-
-#define ERDBOFF (0x20)
-#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF)
-#define ERDBOFF_ERDBOFF_SHIFT (0)
-
-#define BHIOFF (0x28)
-#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF)
-#define BHIOFF_BHIOFF_SHIFT (0)
-
-#define BHIEOFF (0x2C)
-#define BHIEOFF_BHIEOFF_MASK (0xFFFFFFFF)
-#define BHIEOFF_BHIEOFF_SHIFT (0)
-
-#define DEBUGOFF (0x30)
-#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF)
-#define DEBUGOFF_DEBUGOFF_SHIFT (0)
-
-#define MHICTRL (0x38)
-#define MHICTRL_MHISTATE_MASK (0x0000FF00)
-#define MHICTRL_MHISTATE_SHIFT (8)
-#define MHICTRL_RESET_MASK (0x2)
-#define MHICTRL_RESET_SHIFT (1)
-
-#define MHISTATUS (0x48)
-#define MHISTATUS_MHISTATE_MASK (0x0000FF00)
-#define MHISTATUS_MHISTATE_SHIFT (8)
-#define MHISTATUS_SYSERR_MASK (0x4)
-#define MHISTATUS_SYSERR_SHIFT (2)
-#define MHISTATUS_READY_MASK (0x1)
-#define MHISTATUS_READY_SHIFT (0)
-
-#define CCABAP_LOWER (0x58)
-#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF)
-#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0)
-
-#define CCABAP_HIGHER (0x5C)
-#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF)
-#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0)
-
-#define ECABAP_LOWER (0x60)
-#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF)
-#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0)
-
-#define ECABAP_HIGHER (0x64)
-#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF)
-#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0)
-
-#define CRCBAP_LOWER (0x68)
-#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF)
-#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0)
-
-#define CRCBAP_HIGHER (0x6C)
-#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF)
-#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0)
-
-#define CRDB_LOWER (0x70)
-#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF)
-#define CRDB_LOWER_CRDB_LOWER_SHIFT (0)
-
-#define CRDB_HIGHER (0x74)
-#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF)
-#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0)
-
-#define MHICTRLBASE_LOWER (0x80)
-#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF)
-#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0)
-
-#define MHICTRLBASE_HIGHER (0x84)
-#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF)
-#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0)
-
-#define MHICTRLLIMIT_LOWER (0x88)
-#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF)
-#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0)
-
-#define MHICTRLLIMIT_HIGHER (0x8C)
-#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF)
-#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0)
-
-#define MHIDATABASE_LOWER (0x98)
-#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF)
-#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0)
-
-#define MHIDATABASE_HIGHER (0x9C)
-#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF)
-#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0)
-
-#define MHIDATALIMIT_LOWER (0xA0)
-#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF)
-#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0)
-
-#define MHIDATALIMIT_HIGHER (0xA4)
-#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF)
-#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0)
-
-/* Host request register */
-#define MHI_SOC_RESET_REQ_OFFSET (0xB0)
-#define MHI_SOC_RESET_REQ BIT(0)
-
-/* MHI BHI offfsets */
-#define BHI_BHIVERSION_MINOR (0x00)
-#define BHI_BHIVERSION_MAJOR (0x04)
-#define BHI_IMGADDR_LOW (0x08)
-#define BHI_IMGADDR_HIGH (0x0C)
-#define BHI_IMGSIZE (0x10)
-#define BHI_RSVD1 (0x14)
-#define BHI_IMGTXDB (0x18)
-#define BHI_TXDB_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHI_TXDB_SEQNUM_SHFT (0)
-#define BHI_RSVD2 (0x1C)
-#define BHI_INTVEC (0x20)
-#define BHI_RSVD3 (0x24)
-#define BHI_EXECENV (0x28)
-#define BHI_STATUS (0x2C)
-#define BHI_ERRCODE (0x30)
-#define BHI_ERRDBG1 (0x34)
-#define BHI_ERRDBG2 (0x38)
-#define BHI_ERRDBG3 (0x3C)
-#define BHI_SERIALNU (0x40)
-#define BHI_SBLANTIROLLVER (0x44)
-#define BHI_NUMSEG (0x48)
-#define BHI_MSMHWID(n) (0x4C + (0x4 * (n)))
-#define BHI_OEMPKHASH(n) (0x64 + (0x4 * (n)))
-#define BHI_RSVD5 (0xC4)
-#define BHI_STATUS_MASK (0xC0000000)
-#define BHI_STATUS_SHIFT (30)
-#define BHI_STATUS_ERROR (3)
-#define BHI_STATUS_SUCCESS (2)
-#define BHI_STATUS_RESET (0)
-
-/* MHI BHIE offsets */
-#define BHIE_MSMSOCID_OFFS (0x0000)
-#define BHIE_TXVECADDR_LOW_OFFS (0x002C)
-#define BHIE_TXVECADDR_HIGH_OFFS (0x0030)
-#define BHIE_TXVECSIZE_OFFS (0x0034)
-#define BHIE_TXVECDB_OFFS (0x003C)
-#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_TXVECDB_SEQNUM_SHFT (0)
-#define BHIE_TXVECSTATUS_OFFS (0x0044)
-#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
-#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
-#define BHIE_TXVECSTATUS_STATUS_SHFT (30)
-#define BHIE_TXVECSTATUS_STATUS_RESET (0x00)
-#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02)
-#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03)
-#define BHIE_RXVECADDR_LOW_OFFS (0x0060)
-#define BHIE_RXVECADDR_HIGH_OFFS (0x0064)
-#define BHIE_RXVECSIZE_OFFS (0x0068)
-#define BHIE_RXVECDB_OFFS (0x0070)
-#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_RXVECDB_SEQNUM_SHFT (0)
-#define BHIE_RXVECSTATUS_OFFS (0x0078)
-#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
-#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
-#define BHIE_RXVECSTATUS_STATUS_SHFT (30)
-#define BHIE_RXVECSTATUS_STATUS_RESET (0x00)
-#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02)
-#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03)
-
-#define SOC_HW_VERSION_OFFS (0x224)
-#define SOC_HW_VERSION_FAM_NUM_BMSK (0xF0000000)
-#define SOC_HW_VERSION_FAM_NUM_SHFT (28)
-#define SOC_HW_VERSION_DEV_NUM_BMSK (0x0FFF0000)
-#define SOC_HW_VERSION_DEV_NUM_SHFT (16)
-#define SOC_HW_VERSION_MAJOR_VER_BMSK (0x0000FF00)
-#define SOC_HW_VERSION_MAJOR_VER_SHFT (8)
-#define SOC_HW_VERSION_MINOR_VER_BMSK (0x000000FF)
-#define SOC_HW_VERSION_MINOR_VER_SHFT (0)
-
-#define EV_CTX_RESERVED_MASK GENMASK(7, 0)
-#define EV_CTX_INTMODC_MASK GENMASK(15, 8)
-#define EV_CTX_INTMODC_SHIFT 8
-#define EV_CTX_INTMODT_MASK GENMASK(31, 16)
-#define EV_CTX_INTMODT_SHIFT 16
-struct mhi_event_ctxt {
- __u32 intmod;
- __u32 ertype;
- __u32 msivec;
-
- __u64 rbase __packed __aligned(4);
- __u64 rlen __packed __aligned(4);
- __u64 rp __packed __aligned(4);
- __u64 wp __packed __aligned(4);
-};
-
-#define CHAN_CTX_CHSTATE_MASK GENMASK(7, 0)
-#define CHAN_CTX_CHSTATE_SHIFT 0
-#define CHAN_CTX_BRSTMODE_MASK GENMASK(9, 8)
-#define CHAN_CTX_BRSTMODE_SHIFT 8
-#define CHAN_CTX_POLLCFG_MASK GENMASK(15, 10)
-#define CHAN_CTX_POLLCFG_SHIFT 10
-#define CHAN_CTX_RESERVED_MASK GENMASK(31, 16)
-struct mhi_chan_ctxt {
- __u32 chcfg;
- __u32 chtype;
- __u32 erindex;
-
- __u64 rbase __packed __aligned(4);
- __u64 rlen __packed __aligned(4);
- __u64 rp __packed __aligned(4);
- __u64 wp __packed __aligned(4);
-};
-
-struct mhi_cmd_ctxt {
- __u32 reserved0;
- __u32 reserved1;
- __u32 reserved2;
-
- __u64 rbase __packed __aligned(4);
- __u64 rlen __packed __aligned(4);
- __u64 rp __packed __aligned(4);
- __u64 wp __packed __aligned(4);
-};
-
-struct mhi_ctxt {
- struct mhi_event_ctxt *er_ctxt;
- struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_cmd_ctxt *cmd_ctxt;
- dma_addr_t er_ctxt_addr;
- dma_addr_t chan_ctxt_addr;
- dma_addr_t cmd_ctxt_addr;
-};
-
-struct mhi_tre {
- u64 ptr;
- u32 dword[2];
-};
-
-struct bhi_vec_entry {
- u64 dma_addr;
- u64 size;
-};
-
-enum mhi_cmd_type {
- MHI_CMD_NOP = 1,
- MHI_CMD_RESET_CHAN = 16,
- MHI_CMD_STOP_CHAN = 17,
- MHI_CMD_START_CHAN = 18,
-};
-
-/* No operation command */
-#define MHI_TRE_CMD_NOOP_PTR (0)
-#define MHI_TRE_CMD_NOOP_DWORD0 (0)
-#define MHI_TRE_CMD_NOOP_DWORD1 (MHI_CMD_NOP << 16)
-
-/* Channel reset command */
-#define MHI_TRE_CMD_RESET_PTR (0)
-#define MHI_TRE_CMD_RESET_DWORD0 (0)
-#define MHI_TRE_CMD_RESET_DWORD1(chid) ((chid << 24) | \
- (MHI_CMD_RESET_CHAN << 16))
-
-/* Channel stop command */
-#define MHI_TRE_CMD_STOP_PTR (0)
-#define MHI_TRE_CMD_STOP_DWORD0 (0)
-#define MHI_TRE_CMD_STOP_DWORD1(chid) ((chid << 24) | \
- (MHI_CMD_STOP_CHAN << 16))
-
-/* Channel start command */
-#define MHI_TRE_CMD_START_PTR (0)
-#define MHI_TRE_CMD_START_DWORD0 (0)
-#define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \
- (MHI_CMD_START_CHAN << 16))
-
-#define MHI_TRE_GET_CMD_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF)
-#define MHI_TRE_GET_CMD_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF)
-
-/* Event descriptor macros */
-#define MHI_TRE_EV_PTR(ptr) (ptr)
-#define MHI_TRE_EV_DWORD0(code, len) ((code << 24) | len)
-#define MHI_TRE_EV_DWORD1(chid, type) ((chid << 24) | (type << 16))
-#define MHI_TRE_GET_EV_PTR(tre) ((tre)->ptr)
-#define MHI_TRE_GET_EV_CODE(tre) (((tre)->dword[0] >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_LEN(tre) ((tre)->dword[0] & 0xFFFF)
-#define MHI_TRE_GET_EV_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF)
-#define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_SEQ(tre) ((tre)->dword[0])
-#define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr)
-#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr)
-#define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF)
-#define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF)
-
-/* Transfer descriptor macros */
-#define MHI_TRE_DATA_PTR(ptr) (ptr)
-#define MHI_TRE_DATA_DWORD0(len) (len & MHI_MAX_MTU)
-#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) ((2 << 16) | (bei << 10) \
- | (ieot << 9) | (ieob << 8) | chain)
-
-/* RSC transfer descriptor macros */
-#define MHI_RSCTRE_DATA_PTR(ptr, len) (((u64)len << 48) | ptr)
-#define MHI_RSCTRE_DATA_DWORD0(cookie) (cookie)
-#define MHI_RSCTRE_DATA_DWORD1 (MHI_PKT_TYPE_COALESCING << 16)
-
-enum mhi_pkt_type {
- MHI_PKT_TYPE_INVALID = 0x0,
- MHI_PKT_TYPE_NOOP_CMD = 0x1,
- MHI_PKT_TYPE_TRANSFER = 0x2,
- MHI_PKT_TYPE_COALESCING = 0x8,
- MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10,
- MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11,
- MHI_PKT_TYPE_START_CHAN_CMD = 0x12,
- MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20,
- MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21,
- MHI_PKT_TYPE_TX_EVENT = 0x22,
- MHI_PKT_TYPE_RSC_TX_EVENT = 0x28,
- MHI_PKT_TYPE_EE_EVENT = 0x40,
- MHI_PKT_TYPE_TSYNC_EVENT = 0x48,
- MHI_PKT_TYPE_BW_REQ_EVENT = 0x50,
- MHI_PKT_TYPE_STALE_EVENT, /* internal event */
-};
-
-/* MHI transfer completion events */
-enum mhi_ev_ccs {
- MHI_EV_CC_INVALID = 0x0,
- MHI_EV_CC_SUCCESS = 0x1,
- MHI_EV_CC_EOT = 0x2, /* End of transfer event */
- MHI_EV_CC_OVERFLOW = 0x3,
- MHI_EV_CC_EOB = 0x4, /* End of block event */
- MHI_EV_CC_OOB = 0x5, /* Out of block event */
- MHI_EV_CC_DB_MODE = 0x6,
- MHI_EV_CC_UNDEFINED_ERR = 0x10,
- MHI_EV_CC_BAD_TRE = 0x11,
-};
-
-enum mhi_ch_state {
- MHI_CH_STATE_DISABLED = 0x0,
- MHI_CH_STATE_ENABLED = 0x1,
- MHI_CH_STATE_RUNNING = 0x2,
- MHI_CH_STATE_SUSPENDED = 0x3,
- MHI_CH_STATE_STOP = 0x4,
- MHI_CH_STATE_ERROR = 0x5,
-};
-
-enum mhi_ch_state_type {
- MHI_CH_STATE_TYPE_RESET,
- MHI_CH_STATE_TYPE_STOP,
- MHI_CH_STATE_TYPE_START,
- MHI_CH_STATE_TYPE_MAX,
-};
-
-extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
-#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
- "INVALID_STATE" : \
- mhi_ch_state_type_str[(state)])
-
-#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
- mode != MHI_DB_BRST_ENABLE)
-
-extern const char * const mhi_ee_str[MHI_EE_MAX];
-#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
- "INVALID_EE" : mhi_ee_str[ee])
-
-#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \
- ee == MHI_EE_EDL)
-#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS)
-#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL)
-#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \
- ee == MHI_EE_FP)
-
-enum dev_st_transition {
- DEV_ST_TRANSITION_PBL,
- DEV_ST_TRANSITION_READY,
- DEV_ST_TRANSITION_SBL,
- DEV_ST_TRANSITION_MISSION_MODE,
- DEV_ST_TRANSITION_FP,
- DEV_ST_TRANSITION_SYS_ERR,
- DEV_ST_TRANSITION_DISABLE,
- DEV_ST_TRANSITION_MAX,
-};
-
-extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX];
-#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \
- "INVALID_STATE" : dev_state_tran_str[state])
-
-extern const char * const mhi_state_str[MHI_STATE_MAX];
-#define TO_MHI_STATE_STR(state) ((state >= MHI_STATE_MAX || \
- !mhi_state_str[state]) ? \
- "INVALID_STATE" : mhi_state_str[state])
-
-/* internal power states */
-enum mhi_pm_state {
- MHI_PM_STATE_DISABLE,
- MHI_PM_STATE_POR,
- MHI_PM_STATE_M0,
- MHI_PM_STATE_M2,
- MHI_PM_STATE_M3_ENTER,
- MHI_PM_STATE_M3,
- MHI_PM_STATE_M3_EXIT,
- MHI_PM_STATE_FW_DL_ERR,
- MHI_PM_STATE_SYS_ERR_DETECT,
- MHI_PM_STATE_SYS_ERR_PROCESS,
- MHI_PM_STATE_SHUTDOWN_PROCESS,
- MHI_PM_STATE_LD_ERR_FATAL_DETECT,
- MHI_PM_STATE_MAX
-};
-
-#define MHI_PM_DISABLE BIT(0)
-#define MHI_PM_POR BIT(1)
-#define MHI_PM_M0 BIT(2)
-#define MHI_PM_M2 BIT(3)
-#define MHI_PM_M3_ENTER BIT(4)
-#define MHI_PM_M3 BIT(5)
-#define MHI_PM_M3_EXIT BIT(6)
-/* firmware download failure state */
-#define MHI_PM_FW_DL_ERR BIT(7)
-#define MHI_PM_SYS_ERR_DETECT BIT(8)
-#define MHI_PM_SYS_ERR_PROCESS BIT(9)
-#define MHI_PM_SHUTDOWN_PROCESS BIT(10)
-/* link not accessible */
-#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11)
-
-#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
- MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
- MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
- MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
-#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
-#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
-#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & \
- mhi_cntrl->db_access)
-#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
- MHI_PM_M2 | MHI_PM_M3_EXIT))
-#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2)
-#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state)
-#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \
- MHI_PM_IN_ERROR_STATE(pm_state))
-#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \
- (MHI_PM_M3_ENTER | MHI_PM_M3))
-
-#define NR_OF_CMD_RINGS 1
-#define CMD_EL_PER_RING 128
-#define PRIMARY_CMD_RING 0
-#define MHI_DEV_WAKE_DB 127
-#define MHI_MAX_MTU 0xffff
-#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1)
-
-enum mhi_er_type {
- MHI_ER_TYPE_INVALID = 0x0,
- MHI_ER_TYPE_VALID = 0x1,
-};
-
-struct db_cfg {
- bool reset_req;
- bool db_mode;
- u32 pollcfg;
- enum mhi_db_brst_mode brstmode;
- dma_addr_t db_val;
- void (*process_db)(struct mhi_controller *mhi_cntrl,
- struct db_cfg *db_cfg, void __iomem *io_addr,
- dma_addr_t db_val);
-};
-
-struct mhi_pm_transitions {
- enum mhi_pm_state from_state;
- u32 to_states;
-};
-
-struct state_transition {
- struct list_head node;
- enum dev_st_transition state;
-};
-
-struct mhi_ring {
- dma_addr_t dma_handle;
- dma_addr_t iommu_base;
- u64 *ctxt_wp; /* point to ctxt wp */
- void *pre_aligned;
- void *base;
- void *rp;
- void *wp;
- size_t el_size;
- size_t len;
- size_t elements;
- size_t alloc_size;
- void __iomem *db_addr;
-};
-
-struct mhi_cmd {
- struct mhi_ring ring;
- spinlock_t lock;
-};
-
-struct mhi_buf_info {
- void *v_addr;
- void *bb_addr;
- void *wp;
- void *cb_buf;
- dma_addr_t p_addr;
- size_t len;
- enum dma_data_direction dir;
- bool used; /* Indicates whether the buffer is used or not */
- bool pre_mapped; /* Already pre-mapped by client */
-};
-
-struct mhi_event {
- struct mhi_controller *mhi_cntrl;
- struct mhi_chan *mhi_chan; /* dedicated to channel */
- u32 er_index;
- u32 intmod;
- u32 irq;
- int chan; /* this event ring is dedicated to a channel (optional) */
- u32 priority;
- enum mhi_er_data_type data_type;
- struct mhi_ring ring;
- struct db_cfg db_cfg;
- struct tasklet_struct task;
- spinlock_t lock;
- int (*process_event)(struct mhi_controller *mhi_cntrl,
- struct mhi_event *mhi_event,
- u32 event_quota);
- bool hw_ring;
- bool cl_manage;
- bool offload_ev; /* managed by a device driver */
-};
-
-struct mhi_chan {
- const char *name;
- /*
- * Important: When consuming, increment tre_ring first and when
- * releasing, decrement buf_ring first. If tre_ring has space, buf_ring
- * is guranteed to have space so we do not need to check both rings.
- */
- struct mhi_ring buf_ring;
- struct mhi_ring tre_ring;
- u32 chan;
- u32 er_index;
- u32 intmod;
- enum mhi_ch_type type;
- enum dma_data_direction dir;
- struct db_cfg db_cfg;
- enum mhi_ch_ee_mask ee_mask;
- enum mhi_ch_state ch_state;
- enum mhi_ev_ccs ccs;
- struct mhi_device *mhi_dev;
- void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result);
- struct mutex mutex;
- struct completion completion;
- rwlock_t lock;
- struct list_head node;
- bool lpm_notify;
- bool configured;
- bool offload_ch;
- bool pre_alloc;
- bool wake_capable;
-};
-
-/* Default MHI timeout */
-#define MHI_TIMEOUT_MS (1000)
-
-/* debugfs related functions */
-#ifdef CONFIG_MHI_BUS_DEBUG
-void mhi_create_debugfs(struct mhi_controller *mhi_cntrl);
-void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl);
-void mhi_debugfs_init(void);
-void mhi_debugfs_exit(void);
-#else
-static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
-{
-}
-
-static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
-{
-}
-
-static inline void mhi_debugfs_init(void)
-{
-}
-
-static inline void mhi_debugfs_exit(void)
-{
-}
-#endif
-
-struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl);
-
-int mhi_destroy_device(struct device *dev, void *data);
-void mhi_create_devices(struct mhi_controller *mhi_cntrl);
-
-int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
- struct image_info **image_info, size_t alloc_size);
-void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
- struct image_info *image_info);
-
-/* Power management APIs */
-enum mhi_pm_state __must_check mhi_tryset_pm_state(
- struct mhi_controller *mhi_cntrl,
- enum mhi_pm_state state);
-const char *to_mhi_pm_state_str(enum mhi_pm_state state);
-int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
- enum dev_st_transition state);
-void mhi_pm_st_worker(struct work_struct *work);
-void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl);
-int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl);
-int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
-void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl);
-int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
-int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
-int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
- enum mhi_cmd_type cmd);
-int mhi_download_amss_image(struct mhi_controller *mhi_cntrl);
-static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl)
-{
- return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
- mhi_cntrl->dev_state <= MHI_STATE_M3_FAST);
-}
-
-static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl)
-{
- pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
- mhi_cntrl->runtime_get(mhi_cntrl);
- mhi_cntrl->runtime_put(mhi_cntrl);
-}
-
-/* Register access methods */
-void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,
- void __iomem *db_addr, dma_addr_t db_val);
-void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
- struct db_cfg *db_mode, void __iomem *db_addr,
- dma_addr_t db_val);
-int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
- void __iomem *base, u32 offset, u32 *out);
-int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
- void __iomem *base, u32 offset, u32 mask,
- u32 shift, u32 *out);
-int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
- void __iomem *base, u32 offset, u32 mask,
- u32 shift, u32 val, u32 delayus);
-void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
- u32 offset, u32 val);
-void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
- u32 offset, u32 mask, u32 shift, u32 val);
-void mhi_ring_er_db(struct mhi_event *mhi_event);
-void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
- dma_addr_t db_val);
-void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd);
-void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan);
-
-/* Initialization methods */
-int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
-int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl);
-void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl);
-int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl);
-void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
-void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
- struct image_info *img_info);
-void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
-
-/* Automatically allocate and queue inbound buffers */
-#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0)
-int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan, unsigned int flags);
-
-int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan);
-void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan);
-void mhi_reset_chan(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan);
-
-/* Event processing methods */
-void mhi_ctrl_ev_task(unsigned long data);
-void mhi_ev_task(unsigned long data);
-int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
- struct mhi_event *mhi_event, u32 event_quota);
-int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
- struct mhi_event *mhi_event, u32 event_quota);
-
-/* ISR handlers */
-irqreturn_t mhi_irq_handler(int irq_number, void *dev);
-irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev);
-irqreturn_t mhi_intvec_handler(int irq_number, void *dev);
-
-int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
- struct mhi_buf_info *info, enum mhi_flags flags);
-int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
- struct mhi_buf_info *buf_info);
-int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
- struct mhi_buf_info *buf_info);
-void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
- struct mhi_buf_info *buf_info);
-void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
- struct mhi_buf_info *buf_info);
-
-#endif /* _MHI_INT_H */
diff --git a/drivers/bus/mhi/host/Kconfig b/drivers/bus/mhi/host/Kconfig
new file mode 100644
index 000000000000..da5cd0c9fc62
--- /dev/null
+++ b/drivers/bus/mhi/host/Kconfig
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# MHI bus
+#
+# Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+#
+
+config MHI_BUS
+ tristate "Modem Host Interface (MHI) bus"
+ help
+ Bus driver for MHI protocol. Modem Host Interface (MHI) is a
+ communication protocol used by the host processors to control
+ and communicate with modem devices over a high speed peripheral
+ bus or shared memory.
+
+config MHI_BUS_DEBUG
+ bool "Debugfs support for the MHI bus"
+ depends on MHI_BUS && DEBUG_FS
+ help
+ Enable debugfs support for use with the MHI transport. Allows
+ reading and/or modifying some values within the MHI controller
+ for debug and test purposes.
+
+config MHI_BUS_PCI_GENERIC
+ tristate "MHI PCI controller driver"
+ depends on MHI_BUS
+ depends on PCI
+ help
+ This driver provides MHI PCI controller driver for devices such as
+ Qualcomm SDX55 based PCIe modems.
+
diff --git a/drivers/bus/mhi/core/Makefile b/drivers/bus/mhi/host/Makefile
index c3feb4130aa3..859c2f38451c 100644
--- a/drivers/bus/mhi/core/Makefile
+++ b/drivers/bus/mhi/host/Makefile
@@ -1,4 +1,6 @@
obj-$(CONFIG_MHI_BUS) += mhi.o
-
mhi-y := init.o main.o pm.o boot.o
mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o
+
+obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
+mhi_pci_generic-y += pci_generic.o
diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/host/boot.c
index 74295d3cc662..b0da7ca4519c 100644
--- a/drivers/bus/mhi/core/boot.c
+++ b/drivers/bus/mhi/host/boot.c
@@ -46,8 +46,7 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK);
mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
- BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
- sequence_id);
+ BHIE_RXVECDB_SEQNUM_BMSK, sequence_id);
dev_dbg(dev, "Address: %p and len: 0x%zx sequence: %u\n",
&mhi_buf->dma_addr, mhi_buf->len, sequence_id);
@@ -68,7 +67,7 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
dev_dbg(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
- TO_MHI_STATE_STR(mhi_cntrl->dev_state),
+ mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
/*
@@ -127,9 +126,7 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
while (retry--) {
ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS,
- BHIE_RXVECSTATUS_STATUS_BMSK,
- BHIE_RXVECSTATUS_STATUS_SHFT,
- &rx_status);
+ BHIE_RXVECSTATUS_STATUS_BMSK, &rx_status);
if (ret)
return -EIO;
@@ -168,7 +165,6 @@ int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic)
mhi_read_reg_field(mhi_cntrl, base,
BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK,
- BHIE_RXVECSTATUS_STATUS_SHFT,
&rx_status) || rx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
@@ -203,8 +199,7 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
- BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
- sequence_id);
+ BHIE_TXVECDB_SEQNUM_BMSK, sequence_id);
read_unlock_bh(pm_lock);
/* Wait for the image download to complete */
@@ -213,7 +208,6 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
mhi_read_reg_field(mhi_cntrl, base,
BHIE_TXVECSTATUS_OFFS,
BHIE_TXVECSTATUS_STATUS_BMSK,
- BHIE_TXVECSTATUS_STATUS_SHFT,
&tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
@@ -265,8 +259,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
- BHI_STATUS_MASK, BHI_STATUS_SHIFT,
- &tx_status) || tx_status,
+ BHI_STATUS_MASK, &tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
goto invalid_pm_state;
diff --git a/drivers/bus/mhi/core/debugfs.c b/drivers/bus/mhi/host/debugfs.c
index 858d7516410b..cfec7811dfbb 100644
--- a/drivers/bus/mhi/core/debugfs.c
+++ b/drivers/bus/mhi/host/debugfs.c
@@ -20,7 +20,7 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d)
seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",
- TO_MHI_STATE_STR(mhi_cntrl->dev_state),
+ mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
mhi_cntrl->wake_set ? "true" : "false");
@@ -60,16 +60,16 @@ static int mhi_debugfs_events_show(struct seq_file *m, void *d)
}
seq_printf(m, "Index: %d intmod count: %lu time: %lu",
- i, (er_ctxt->intmod & EV_CTX_INTMODC_MASK) >>
- EV_CTX_INTMODC_SHIFT,
- (er_ctxt->intmod & EV_CTX_INTMODT_MASK) >>
- EV_CTX_INTMODT_SHIFT);
+ i, (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODC_MASK) >>
+ __ffs(EV_CTX_INTMODC_MASK),
+ (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODT_MASK) >>
+ __ffs(EV_CTX_INTMODT_MASK));
- seq_printf(m, " base: 0x%0llx len: 0x%llx", er_ctxt->rbase,
- er_ctxt->rlen);
+ seq_printf(m, " base: 0x%0llx len: 0x%llx", le64_to_cpu(er_ctxt->rbase),
+ le64_to_cpu(er_ctxt->rlen));
- seq_printf(m, " rp: 0x%llx wp: 0x%llx", er_ctxt->rp,
- er_ctxt->wp);
+ seq_printf(m, " rp: 0x%llx wp: 0x%llx", le64_to_cpu(er_ctxt->rp),
+ le64_to_cpu(er_ctxt->wp));
seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp,
&mhi_event->db_cfg.db_val);
@@ -106,18 +106,18 @@ static int mhi_debugfs_channels_show(struct seq_file *m, void *d)
seq_printf(m,
"%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx",
- mhi_chan->name, mhi_chan->chan, (chan_ctxt->chcfg &
- CHAN_CTX_CHSTATE_MASK) >> CHAN_CTX_CHSTATE_SHIFT,
- (chan_ctxt->chcfg & CHAN_CTX_BRSTMODE_MASK) >>
- CHAN_CTX_BRSTMODE_SHIFT, (chan_ctxt->chcfg &
- CHAN_CTX_POLLCFG_MASK) >> CHAN_CTX_POLLCFG_SHIFT);
+ mhi_chan->name, mhi_chan->chan, (le32_to_cpu(chan_ctxt->chcfg) &
+ CHAN_CTX_CHSTATE_MASK) >> __ffs(CHAN_CTX_CHSTATE_MASK),
+ (le32_to_cpu(chan_ctxt->chcfg) & CHAN_CTX_BRSTMODE_MASK) >>
+ __ffs(CHAN_CTX_BRSTMODE_MASK), (le32_to_cpu(chan_ctxt->chcfg) &
+ CHAN_CTX_POLLCFG_MASK) >> __ffs(CHAN_CTX_POLLCFG_MASK));
- seq_printf(m, " type: 0x%x event ring: %u", chan_ctxt->chtype,
- chan_ctxt->erindex);
+ seq_printf(m, " type: 0x%x event ring: %u", le32_to_cpu(chan_ctxt->chtype),
+ le32_to_cpu(chan_ctxt->erindex));
seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx",
- chan_ctxt->rbase, chan_ctxt->rlen, chan_ctxt->rp,
- chan_ctxt->wp);
+ le64_to_cpu(chan_ctxt->rbase), le64_to_cpu(chan_ctxt->rlen),
+ le64_to_cpu(chan_ctxt->rp), le64_to_cpu(chan_ctxt->wp));
seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n",
ring->rp, ring->wp,
@@ -206,13 +206,13 @@ static int mhi_debugfs_regdump_show(struct seq_file *m, void *d)
seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
- TO_MHI_STATE_STR(mhi_cntrl->dev_state),
+ mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_get_exec_env(mhi_cntrl);
seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee),
- TO_MHI_STATE_STR(state));
+ mhi_state_str(state));
for (i = 0; regs[i].name; i++) {
if (!regs[i].base)
diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/host/init.c
index 046f407dc5d6..a665b8e92408 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/host/init.c
@@ -4,6 +4,7 @@
*
*/
+#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
@@ -44,18 +45,6 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
};
-const char * const mhi_state_str[MHI_STATE_MAX] = {
- [MHI_STATE_RESET] = "RESET",
- [MHI_STATE_READY] = "READY",
- [MHI_STATE_M0] = "M0",
- [MHI_STATE_M1] = "M1",
- [MHI_STATE_M2] = "M2",
- [MHI_STATE_M3] = "M3",
- [MHI_STATE_M3_FAST] = "M3 FAST",
- [MHI_STATE_BHI] = "BHI",
- [MHI_STATE_SYS_ERR] = "SYS ERROR",
-};
-
const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
[MHI_CH_STATE_TYPE_RESET] = "RESET",
[MHI_CH_STATE_TYPE_STOP] = "STOP",
@@ -77,12 +66,14 @@ static const char * const mhi_pm_state_str[] = {
[MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect",
};
-const char *to_mhi_pm_state_str(enum mhi_pm_state state)
+const char *to_mhi_pm_state_str(u32 state)
{
- unsigned long pm_state = state;
- int index = find_last_bit(&pm_state, 32);
+ int index;
+
+ if (state)
+ index = __fls(state);
- if (index >= ARRAY_SIZE(mhi_pm_state_str))
+ if (!state || index >= ARRAY_SIZE(mhi_pm_state_str))
return "Invalid State";
return mhi_pm_state_str[index];
@@ -291,17 +282,17 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
if (mhi_chan->offload_ch)
continue;
- tmp = chan_ctxt->chcfg;
+ tmp = le32_to_cpu(chan_ctxt->chcfg);
tmp &= ~CHAN_CTX_CHSTATE_MASK;
- tmp |= (MHI_CH_STATE_DISABLED << CHAN_CTX_CHSTATE_SHIFT);
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
tmp &= ~CHAN_CTX_BRSTMODE_MASK;
- tmp |= (mhi_chan->db_cfg.brstmode << CHAN_CTX_BRSTMODE_SHIFT);
+ tmp |= FIELD_PREP(CHAN_CTX_BRSTMODE_MASK, mhi_chan->db_cfg.brstmode);
tmp &= ~CHAN_CTX_POLLCFG_MASK;
- tmp |= (mhi_chan->db_cfg.pollcfg << CHAN_CTX_POLLCFG_SHIFT);
- chan_ctxt->chcfg = tmp;
+ tmp |= FIELD_PREP(CHAN_CTX_POLLCFG_MASK, mhi_chan->db_cfg.pollcfg);
+ chan_ctxt->chcfg = cpu_to_le32(tmp);
- chan_ctxt->chtype = mhi_chan->type;
- chan_ctxt->erindex = mhi_chan->er_index;
+ chan_ctxt->chtype = cpu_to_le32(mhi_chan->type);
+ chan_ctxt->erindex = cpu_to_le32(mhi_chan->er_index);
mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
mhi_chan->tre_ring.db_addr = (void __iomem *)&chan_ctxt->wp;
@@ -326,17 +317,17 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
if (mhi_event->offload_ev)
continue;
- tmp = er_ctxt->intmod;
+ tmp = le32_to_cpu(er_ctxt->intmod);
tmp &= ~EV_CTX_INTMODC_MASK;
tmp &= ~EV_CTX_INTMODT_MASK;
- tmp |= (mhi_event->intmod << EV_CTX_INTMODT_SHIFT);
- er_ctxt->intmod = tmp;
+ tmp |= FIELD_PREP(EV_CTX_INTMODT_MASK, mhi_event->intmod);
+ er_ctxt->intmod = cpu_to_le32(tmp);
- er_ctxt->ertype = MHI_ER_TYPE_VALID;
- er_ctxt->msivec = mhi_event->irq;
+ er_ctxt->ertype = cpu_to_le32(MHI_ER_TYPE_VALID);
+ er_ctxt->msivec = cpu_to_le32(mhi_event->irq);
mhi_event->db_cfg.db_mode = true;
- ring->el_size = sizeof(struct mhi_tre);
+ ring->el_size = sizeof(struct mhi_ring_element);
ring->len = ring->el_size * ring->elements;
ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len);
if (ret)
@@ -347,9 +338,9 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
* ring is empty
*/
ring->rp = ring->wp = ring->base;
- er_ctxt->rbase = ring->iommu_base;
+ er_ctxt->rbase = cpu_to_le64(ring->iommu_base);
er_ctxt->rp = er_ctxt->wp = er_ctxt->rbase;
- er_ctxt->rlen = ring->len;
+ er_ctxt->rlen = cpu_to_le64(ring->len);
ring->ctxt_wp = &er_ctxt->wp;
}
@@ -368,7 +359,7 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) {
struct mhi_ring *ring = &mhi_cmd->ring;
- ring->el_size = sizeof(struct mhi_tre);
+ ring->el_size = sizeof(struct mhi_ring_element);
ring->elements = CMD_EL_PER_RING;
ring->len = ring->el_size * ring->elements;
ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len);
@@ -376,9 +367,9 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
goto error_alloc_cmd;
ring->rp = ring->wp = ring->base;
- cmd_ctxt->rbase = ring->iommu_base;
+ cmd_ctxt->rbase = cpu_to_le64(ring->iommu_base);
cmd_ctxt->rp = cmd_ctxt->wp = cmd_ctxt->rbase;
- cmd_ctxt->rlen = ring->len;
+ cmd_ctxt->rlen = cpu_to_le64(ring->len);
ring->ctxt_wp = &cmd_ctxt->wp;
}
@@ -435,71 +426,70 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
struct {
u32 offset;
u32 mask;
- u32 shift;
u32 val;
} reg_info[] = {
{
- CCABAP_HIGHER, U32_MAX, 0,
+ CCABAP_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr),
},
{
- CCABAP_LOWER, U32_MAX, 0,
+ CCABAP_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr),
},
{
- ECABAP_HIGHER, U32_MAX, 0,
+ ECABAP_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr),
},
{
- ECABAP_LOWER, U32_MAX, 0,
+ ECABAP_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr),
},
{
- CRCBAP_HIGHER, U32_MAX, 0,
+ CRCBAP_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr),
},
{
- CRCBAP_LOWER, U32_MAX, 0,
+ CRCBAP_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr),
},
{
- MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT,
+ MHICFG, MHICFG_NER_MASK,
mhi_cntrl->total_ev_rings,
},
{
- MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT,
+ MHICFG, MHICFG_NHWER_MASK,
mhi_cntrl->hw_ev_rings,
},
{
- MHICTRLBASE_HIGHER, U32_MAX, 0,
+ MHICTRLBASE_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->iova_start),
},
{
- MHICTRLBASE_LOWER, U32_MAX, 0,
+ MHICTRLBASE_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->iova_start),
},
{
- MHIDATABASE_HIGHER, U32_MAX, 0,
+ MHIDATABASE_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->iova_start),
},
{
- MHIDATABASE_LOWER, U32_MAX, 0,
+ MHIDATABASE_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->iova_start),
},
{
- MHICTRLLIMIT_HIGHER, U32_MAX, 0,
+ MHICTRLLIMIT_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->iova_stop),
},
{
- MHICTRLLIMIT_LOWER, U32_MAX, 0,
+ MHICTRLLIMIT_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->iova_stop),
},
{
- MHIDATALIMIT_HIGHER, U32_MAX, 0,
+ MHIDATALIMIT_HIGHER, U32_MAX,
upper_32_bits(mhi_cntrl->iova_stop),
},
{
- MHIDATALIMIT_LOWER, U32_MAX, 0,
+ MHIDATALIMIT_LOWER, U32_MAX,
lower_32_bits(mhi_cntrl->iova_stop),
},
{ 0, 0, 0 }
@@ -508,8 +498,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
dev_dbg(dev, "Initializing MHI registers\n");
/* Read channel db offset */
- ret = mhi_read_reg_field(mhi_cntrl, base, CHDBOFF, CHDBOFF_CHDBOFF_MASK,
- CHDBOFF_CHDBOFF_SHIFT, &val);
+ ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val);
if (ret) {
dev_err(dev, "Unable to read CHDBOFF register\n");
return -EIO;
@@ -525,8 +514,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
mhi_chan->tre_ring.db_addr = base + val;
/* Read event ring db offset */
- ret = mhi_read_reg_field(mhi_cntrl, base, ERDBOFF, ERDBOFF_ERDBOFF_MASK,
- ERDBOFF_ERDBOFF_SHIFT, &val);
+ ret = mhi_read_reg(mhi_cntrl, base, ERDBOFF, &val);
if (ret) {
dev_err(dev, "Unable to read ERDBOFF register\n");
return -EIO;
@@ -547,8 +535,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
/* Write to MMIO registers */
for (i = 0; reg_info[i].offset; i++)
mhi_write_reg_field(mhi_cntrl, base, reg_info[i].offset,
- reg_info[i].mask, reg_info[i].shift,
- reg_info[i].val);
+ reg_info[i].mask, reg_info[i].val);
return 0;
}
@@ -579,10 +566,10 @@ void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
chan_ctxt->rp = 0;
chan_ctxt->wp = 0;
- tmp = chan_ctxt->chcfg;
+ tmp = le32_to_cpu(chan_ctxt->chcfg);
tmp &= ~CHAN_CTX_CHSTATE_MASK;
- tmp |= (MHI_CH_STATE_DISABLED << CHAN_CTX_CHSTATE_SHIFT);
- chan_ctxt->chcfg = tmp;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
+ chan_ctxt->chcfg = cpu_to_le32(tmp);
/* Update to all cores */
smp_wmb();
@@ -599,7 +586,7 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
buf_ring = &mhi_chan->buf_ring;
tre_ring = &mhi_chan->tre_ring;
- tre_ring->el_size = sizeof(struct mhi_tre);
+ tre_ring->el_size = sizeof(struct mhi_ring_element);
tre_ring->len = tre_ring->el_size * tre_ring->elements;
chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan];
ret = mhi_alloc_aligned_ring(mhi_cntrl, tre_ring, tre_ring->len);
@@ -616,14 +603,14 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
return -ENOMEM;
}
- tmp = chan_ctxt->chcfg;
+ tmp = le32_to_cpu(chan_ctxt->chcfg);
tmp &= ~CHAN_CTX_CHSTATE_MASK;
- tmp |= (MHI_CH_STATE_ENABLED << CHAN_CTX_CHSTATE_SHIFT);
- chan_ctxt->chcfg = tmp;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_ENABLED);
+ chan_ctxt->chcfg = cpu_to_le32(tmp);
- chan_ctxt->rbase = tre_ring->iommu_base;
+ chan_ctxt->rbase = cpu_to_le64(tre_ring->iommu_base);
chan_ctxt->rp = chan_ctxt->wp = chan_ctxt->rbase;
- chan_ctxt->rlen = tre_ring->len;
+ chan_ctxt->rlen = cpu_to_le64(tre_ring->len);
tre_ring->ctxt_wp = &chan_ctxt->wp;
tre_ring->rp = tre_ring->wp = tre_ring->base;
@@ -962,14 +949,10 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
if (ret)
goto err_destroy_wq;
- mhi_cntrl->family_number = (soc_info & SOC_HW_VERSION_FAM_NUM_BMSK) >>
- SOC_HW_VERSION_FAM_NUM_SHFT;
- mhi_cntrl->device_number = (soc_info & SOC_HW_VERSION_DEV_NUM_BMSK) >>
- SOC_HW_VERSION_DEV_NUM_SHFT;
- mhi_cntrl->major_version = (soc_info & SOC_HW_VERSION_MAJOR_VER_BMSK) >>
- SOC_HW_VERSION_MAJOR_VER_SHFT;
- mhi_cntrl->minor_version = (soc_info & SOC_HW_VERSION_MINOR_VER_BMSK) >>
- SOC_HW_VERSION_MINOR_VER_SHFT;
+ mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info);
+ mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info);
+ mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info);
+ mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info);
mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL);
if (mhi_cntrl->index < 0) {
diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
new file mode 100644
index 000000000000..b47d8ef2624a
--- /dev/null
+++ b/drivers/bus/mhi/host/internal.h
@@ -0,0 +1,382 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef _MHI_INT_H
+#define _MHI_INT_H
+
+#include "../common.h"
+
+extern struct bus_type mhi_bus_type;
+
+/* Host request register */
+#define MHI_SOC_RESET_REQ_OFFSET 0xb0
+#define MHI_SOC_RESET_REQ BIT(0)
+
+#define SOC_HW_VERSION_OFFS 0x224
+#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28)
+#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16)
+#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8)
+#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0)
+
+struct mhi_ctxt {
+ struct mhi_event_ctxt *er_ctxt;
+ struct mhi_chan_ctxt *chan_ctxt;
+ struct mhi_cmd_ctxt *cmd_ctxt;
+ dma_addr_t er_ctxt_addr;
+ dma_addr_t chan_ctxt_addr;
+ dma_addr_t cmd_ctxt_addr;
+};
+
+struct bhi_vec_entry {
+ u64 dma_addr;
+ u64 size;
+};
+
+enum mhi_ch_state_type {
+ MHI_CH_STATE_TYPE_RESET,
+ MHI_CH_STATE_TYPE_STOP,
+ MHI_CH_STATE_TYPE_START,
+ MHI_CH_STATE_TYPE_MAX,
+};
+
+extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
+#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
+ "INVALID_STATE" : \
+ mhi_ch_state_type_str[(state)])
+
+#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
+ mode != MHI_DB_BRST_ENABLE)
+
+extern const char * const mhi_ee_str[MHI_EE_MAX];
+#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
+ "INVALID_EE" : mhi_ee_str[ee])
+
+#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \
+ ee == MHI_EE_EDL)
+#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS)
+#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL)
+#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \
+ ee == MHI_EE_FP)
+
+enum dev_st_transition {
+ DEV_ST_TRANSITION_PBL,
+ DEV_ST_TRANSITION_READY,
+ DEV_ST_TRANSITION_SBL,
+ DEV_ST_TRANSITION_MISSION_MODE,
+ DEV_ST_TRANSITION_FP,
+ DEV_ST_TRANSITION_SYS_ERR,
+ DEV_ST_TRANSITION_DISABLE,
+ DEV_ST_TRANSITION_MAX,
+};
+
+extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX];
+#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \
+ "INVALID_STATE" : dev_state_tran_str[state])
+
+/* internal power states */
+enum mhi_pm_state {
+ MHI_PM_STATE_DISABLE,
+ MHI_PM_STATE_POR,
+ MHI_PM_STATE_M0,
+ MHI_PM_STATE_M2,
+ MHI_PM_STATE_M3_ENTER,
+ MHI_PM_STATE_M3,
+ MHI_PM_STATE_M3_EXIT,
+ MHI_PM_STATE_FW_DL_ERR,
+ MHI_PM_STATE_SYS_ERR_DETECT,
+ MHI_PM_STATE_SYS_ERR_PROCESS,
+ MHI_PM_STATE_SHUTDOWN_PROCESS,
+ MHI_PM_STATE_LD_ERR_FATAL_DETECT,
+ MHI_PM_STATE_MAX
+};
+
+#define MHI_PM_DISABLE BIT(0)
+#define MHI_PM_POR BIT(1)
+#define MHI_PM_M0 BIT(2)
+#define MHI_PM_M2 BIT(3)
+#define MHI_PM_M3_ENTER BIT(4)
+#define MHI_PM_M3 BIT(5)
+#define MHI_PM_M3_EXIT BIT(6)
+/* firmware download failure state */
+#define MHI_PM_FW_DL_ERR BIT(7)
+#define MHI_PM_SYS_ERR_DETECT BIT(8)
+#define MHI_PM_SYS_ERR_PROCESS BIT(9)
+#define MHI_PM_SHUTDOWN_PROCESS BIT(10)
+/* link not accessible */
+#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11)
+
+#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
+ MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
+ MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
+ MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
+#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
+#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
+#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access)
+#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
+ MHI_PM_M2 | MHI_PM_M3_EXIT))
+#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2)
+#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state)
+#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \
+ MHI_PM_IN_ERROR_STATE(pm_state))
+#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \
+ (MHI_PM_M3_ENTER | MHI_PM_M3))
+
+#define NR_OF_CMD_RINGS 1
+#define CMD_EL_PER_RING 128
+#define PRIMARY_CMD_RING 0
+#define MHI_DEV_WAKE_DB 127
+#define MHI_MAX_MTU 0xffff
+#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1)
+
+enum mhi_er_type {
+ MHI_ER_TYPE_INVALID = 0x0,
+ MHI_ER_TYPE_VALID = 0x1,
+};
+
+struct db_cfg {
+ bool reset_req;
+ bool db_mode;
+ u32 pollcfg;
+ enum mhi_db_brst_mode brstmode;
+ dma_addr_t db_val;
+ void (*process_db)(struct mhi_controller *mhi_cntrl,
+ struct db_cfg *db_cfg, void __iomem *io_addr,
+ dma_addr_t db_val);
+};
+
+struct mhi_pm_transitions {
+ enum mhi_pm_state from_state;
+ u32 to_states;
+};
+
+struct state_transition {
+ struct list_head node;
+ enum dev_st_transition state;
+};
+
+struct mhi_ring {
+ dma_addr_t dma_handle;
+ dma_addr_t iommu_base;
+ __le64 *ctxt_wp; /* point to ctxt wp */
+ void *pre_aligned;
+ void *base;
+ void *rp;
+ void *wp;
+ size_t el_size;
+ size_t len;
+ size_t elements;
+ size_t alloc_size;
+ void __iomem *db_addr;
+};
+
+struct mhi_cmd {
+ struct mhi_ring ring;
+ spinlock_t lock;
+};
+
+struct mhi_buf_info {
+ void *v_addr;
+ void *bb_addr;
+ void *wp;
+ void *cb_buf;
+ dma_addr_t p_addr;
+ size_t len;
+ enum dma_data_direction dir;
+ bool used; /* Indicates whether the buffer is used or not */
+ bool pre_mapped; /* Already pre-mapped by client */
+};
+
+struct mhi_event {
+ struct mhi_controller *mhi_cntrl;
+ struct mhi_chan *mhi_chan; /* dedicated to channel */
+ u32 er_index;
+ u32 intmod;
+ u32 irq;
+ int chan; /* this event ring is dedicated to a channel (optional) */
+ u32 priority;
+ enum mhi_er_data_type data_type;
+ struct mhi_ring ring;
+ struct db_cfg db_cfg;
+ struct tasklet_struct task;
+ spinlock_t lock;
+ int (*process_event)(struct mhi_controller *mhi_cntrl,
+ struct mhi_event *mhi_event,
+ u32 event_quota);
+ bool hw_ring;
+ bool cl_manage;
+ bool offload_ev; /* managed by a device driver */
+};
+
+struct mhi_chan {
+ const char *name;
+ /*
+ * Important: When consuming, increment tre_ring first and when
+ * releasing, decrement buf_ring first. If tre_ring has space, buf_ring
+ * is guranteed to have space so we do not need to check both rings.
+ */
+ struct mhi_ring buf_ring;
+ struct mhi_ring tre_ring;
+ u32 chan;
+ u32 er_index;
+ u32 intmod;
+ enum mhi_ch_type type;
+ enum dma_data_direction dir;
+ struct db_cfg db_cfg;
+ enum mhi_ch_ee_mask ee_mask;
+ enum mhi_ch_state ch_state;
+ enum mhi_ev_ccs ccs;
+ struct mhi_device *mhi_dev;
+ void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result);
+ struct mutex mutex;
+ struct completion completion;
+ rwlock_t lock;
+ struct list_head node;
+ bool lpm_notify;
+ bool configured;
+ bool offload_ch;
+ bool pre_alloc;
+ bool wake_capable;
+};
+
+/* Default MHI timeout */
+#define MHI_TIMEOUT_MS (1000)
+
+/* debugfs related functions */
+#ifdef CONFIG_MHI_BUS_DEBUG
+void mhi_create_debugfs(struct mhi_controller *mhi_cntrl);
+void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl);
+void mhi_debugfs_init(void);
+void mhi_debugfs_exit(void);
+#else
+static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline void mhi_debugfs_init(void)
+{
+}
+
+static inline void mhi_debugfs_exit(void)
+{
+}
+#endif
+
+struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl);
+
+int mhi_destroy_device(struct device *dev, void *data);
+void mhi_create_devices(struct mhi_controller *mhi_cntrl);
+
+int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
+ struct image_info **image_info, size_t alloc_size);
+void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
+ struct image_info *image_info);
+
+/* Power management APIs */
+enum mhi_pm_state __must_check mhi_tryset_pm_state(
+ struct mhi_controller *mhi_cntrl,
+ enum mhi_pm_state state);
+const char *to_mhi_pm_state_str(u32 state);
+int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
+ enum dev_st_transition state);
+void mhi_pm_st_worker(struct work_struct *work);
+void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl);
+int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl);
+int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
+void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl);
+int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
+int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
+int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
+ enum mhi_cmd_type cmd);
+int mhi_download_amss_image(struct mhi_controller *mhi_cntrl);
+static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl)
+{
+ return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
+ mhi_cntrl->dev_state <= MHI_STATE_M3_FAST);
+}
+
+static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl)
+{
+ pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
+ mhi_cntrl->runtime_get(mhi_cntrl);
+ mhi_cntrl->runtime_put(mhi_cntrl);
+}
+
+/* Register access methods */
+void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,
+ void __iomem *db_addr, dma_addr_t db_val);
+void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
+ struct db_cfg *db_mode, void __iomem *db_addr,
+ dma_addr_t db_val);
+int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
+ void __iomem *base, u32 offset, u32 *out);
+int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
+ void __iomem *base, u32 offset, u32 mask,
+ u32 *out);
+int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
+ void __iomem *base, u32 offset, u32 mask,
+ u32 val, u32 delayus);
+void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
+ u32 offset, u32 val);
+void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
+ u32 offset, u32 mask, u32 val);
+void mhi_ring_er_db(struct mhi_event *mhi_event);
+void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
+ dma_addr_t db_val);
+void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd);
+void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan);
+
+/* Initialization methods */
+int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
+int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl);
+void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl);
+int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl);
+void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
+void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
+ struct image_info *img_info);
+void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
+
+/* Automatically allocate and queue inbound buffers */
+#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0)
+int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan, unsigned int flags);
+
+int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan);
+void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan);
+void mhi_reset_chan(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan);
+
+/* Event processing methods */
+void mhi_ctrl_ev_task(unsigned long data);
+void mhi_ev_task(unsigned long data);
+int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
+ struct mhi_event *mhi_event, u32 event_quota);
+int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
+ struct mhi_event *mhi_event, u32 event_quota);
+
+/* ISR handlers */
+irqreturn_t mhi_irq_handler(int irq_number, void *dev);
+irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev);
+irqreturn_t mhi_intvec_handler(int irq_number, void *dev);
+
+int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
+ struct mhi_buf_info *info, enum mhi_flags flags);
+int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
+ struct mhi_buf_info *buf_info);
+int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
+ struct mhi_buf_info *buf_info);
+void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
+ struct mhi_buf_info *buf_info);
+void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
+ struct mhi_buf_info *buf_info);
+
+#endif /* _MHI_INT_H */
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/host/main.c
index ffde617f93a3..9021be7f2359 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -24,7 +24,7 @@ int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset,
- u32 mask, u32 shift, u32 *out)
+ u32 mask, u32 *out)
{
u32 tmp;
int ret;
@@ -33,21 +33,20 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
if (ret)
return ret;
- *out = (tmp & mask) >> shift;
+ *out = (tmp & mask) >> __ffs(mask);
return 0;
}
int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset,
- u32 mask, u32 shift, u32 val, u32 delayus)
+ u32 mask, u32 val, u32 delayus)
{
int ret;
u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
while (retry--) {
- ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, shift,
- &out);
+ ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out);
if (ret)
return ret;
@@ -67,7 +66,7 @@ void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
}
void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
- u32 offset, u32 mask, u32 shift, u32 val)
+ u32 offset, u32 mask, u32 val)
{
int ret;
u32 tmp;
@@ -77,7 +76,7 @@ void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
return;
tmp &= ~mask;
- tmp |= (val << shift);
+ tmp |= (val << __ffs(mask));
mhi_write_reg(mhi_cntrl, base, offset, tmp);
}
@@ -114,7 +113,7 @@ void mhi_ring_er_db(struct mhi_event *mhi_event)
struct mhi_ring *ring = &mhi_event->ring;
mhi_event->db_cfg.process_db(mhi_event->mhi_cntrl, &mhi_event->db_cfg,
- ring->db_addr, *ring->ctxt_wp);
+ ring->db_addr, le64_to_cpu(*ring->ctxt_wp));
}
void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd)
@@ -123,7 +122,7 @@ void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd)
struct mhi_ring *ring = &mhi_cmd->ring;
db = ring->iommu_base + (ring->wp - ring->base);
- *ring->ctxt_wp = db;
+ *ring->ctxt_wp = cpu_to_le64(db);
mhi_write_db(mhi_cntrl, ring->db_addr, db);
}
@@ -140,7 +139,7 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
* before letting h/w know there is new element to fetch.
*/
dma_wmb();
- *ring->ctxt_wp = db;
+ *ring->ctxt_wp = cpu_to_le64(db);
mhi_chan->db_cfg.process_db(mhi_cntrl, &mhi_chan->db_cfg,
ring->db_addr, db);
@@ -159,8 +158,7 @@ enum mhi_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl)
{
u32 state;
int ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
- MHISTATUS_MHISTATE_MASK,
- MHISTATUS_MHISTATE_SHIFT, &state);
+ MHISTATUS_MHISTATE_MASK, &state);
return ret ? MHI_STATE_MAX : state;
}
EXPORT_SYMBOL_GPL(mhi_get_mhi_state);
@@ -432,7 +430,7 @@ irqreturn_t mhi_irq_handler(int irq_number, void *dev)
struct mhi_event_ctxt *er_ctxt =
&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
struct mhi_ring *ev_ring = &mhi_event->ring;
- dma_addr_t ptr = er_ctxt->rp;
+ dma_addr_t ptr = le64_to_cpu(er_ctxt->rp);
void *dev_rp;
if (!is_valid_ring_ptr(ev_ring, ptr)) {
@@ -479,8 +477,8 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
ee = mhi_get_exec_env(mhi_cntrl);
dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n",
TO_MHI_EXEC_STR(mhi_cntrl->ee),
- TO_MHI_STATE_STR(mhi_cntrl->dev_state),
- TO_MHI_EXEC_STR(ee), TO_MHI_STATE_STR(state));
+ mhi_state_str(mhi_cntrl->dev_state),
+ TO_MHI_EXEC_STR(ee), mhi_state_str(state));
if (state == MHI_STATE_SYS_ERR) {
dev_dbg(dev, "System error detected\n");
@@ -537,14 +535,14 @@ static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl,
/* Update the WP */
ring->wp += ring->el_size;
- ctxt_wp = *ring->ctxt_wp + ring->el_size;
+ ctxt_wp = le64_to_cpu(*ring->ctxt_wp) + ring->el_size;
if (ring->wp >= (ring->base + ring->len)) {
ring->wp = ring->base;
ctxt_wp = ring->iommu_base;
}
- *ring->ctxt_wp = ctxt_wp;
+ *ring->ctxt_wp = cpu_to_le64(ctxt_wp);
/* Update the RP */
ring->rp += ring->el_size;
@@ -556,7 +554,7 @@ static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl,
}
static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
- struct mhi_tre *event,
+ struct mhi_ring_element *event,
struct mhi_chan *mhi_chan)
{
struct mhi_ring *buf_ring, *tre_ring;
@@ -592,7 +590,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
case MHI_EV_CC_EOT:
{
dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event);
- struct mhi_tre *local_rp, *ev_tre;
+ struct mhi_ring_element *local_rp, *ev_tre;
void *dev_rp;
struct mhi_buf_info *buf_info;
u16 xfer_len;
@@ -691,7 +689,7 @@ end_process_tx_event:
}
static int parse_rsc_event(struct mhi_controller *mhi_cntrl,
- struct mhi_tre *event,
+ struct mhi_ring_element *event,
struct mhi_chan *mhi_chan)
{
struct mhi_ring *buf_ring, *tre_ring;
@@ -755,12 +753,12 @@ end_process_rsc_event:
}
static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
- struct mhi_tre *tre)
+ struct mhi_ring_element *tre)
{
dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre);
struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
struct mhi_ring *mhi_ring = &cmd_ring->ring;
- struct mhi_tre *cmd_pkt;
+ struct mhi_ring_element *cmd_pkt;
struct mhi_chan *mhi_chan;
u32 chan;
@@ -793,7 +791,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event,
u32 event_quota)
{
- struct mhi_tre *dev_rp, *local_rp;
+ struct mhi_ring_element *dev_rp, *local_rp;
struct mhi_ring *ev_ring = &mhi_event->ring;
struct mhi_event_ctxt *er_ctxt =
&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
@@ -801,7 +799,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 chan;
int count = 0;
- dma_addr_t ptr = er_ctxt->rp;
+ dma_addr_t ptr = le64_to_cpu(er_ctxt->rp);
/*
* This is a quick check to avoid unnecessary event processing
@@ -846,7 +844,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
new_state = MHI_TRE_GET_EV_STATE(local_rp);
dev_dbg(dev, "State change event to state: %s\n",
- TO_MHI_STATE_STR(new_state));
+ mhi_state_str(new_state));
switch (new_state) {
case MHI_STATE_M0:
@@ -873,7 +871,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
}
default:
dev_err(dev, "Invalid state: %s\n",
- TO_MHI_STATE_STR(new_state));
+ mhi_state_str(new_state));
}
break;
@@ -940,7 +938,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
local_rp = ev_ring->rp;
- ptr = er_ctxt->rp;
+ ptr = le64_to_cpu(er_ctxt->rp);
if (!is_valid_ring_ptr(ev_ring, ptr)) {
dev_err(&mhi_cntrl->mhi_dev->dev,
"Event ring rp points outside of the event ring\n");
@@ -963,14 +961,14 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event,
u32 event_quota)
{
- struct mhi_tre *dev_rp, *local_rp;
+ struct mhi_ring_element *dev_rp, *local_rp;
struct mhi_ring *ev_ring = &mhi_event->ring;
struct mhi_event_ctxt *er_ctxt =
&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
int count = 0;
u32 chan;
struct mhi_chan *mhi_chan;
- dma_addr_t ptr = er_ctxt->rp;
+ dma_addr_t ptr = le64_to_cpu(er_ctxt->rp);
if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state)))
return -EIO;
@@ -1011,7 +1009,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
local_rp = ev_ring->rp;
- ptr = er_ctxt->rp;
+ ptr = le64_to_cpu(er_ctxt->rp);
if (!is_valid_ring_ptr(ev_ring, ptr)) {
dev_err(&mhi_cntrl->mhi_dev->dev,
"Event ring rp points outside of the event ring\n");
@@ -1187,7 +1185,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_buf_info *info, enum mhi_flags flags)
{
struct mhi_ring *buf_ring, *tre_ring;
- struct mhi_tre *mhi_tre;
+ struct mhi_ring_element *mhi_tre;
struct mhi_buf_info *buf_info;
int eot, eob, chain, bei;
int ret;
@@ -1258,7 +1256,7 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan,
enum mhi_cmd_type cmd)
{
- struct mhi_tre *cmd_tre = NULL;
+ struct mhi_ring_element *cmd_tre = NULL;
struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
struct mhi_ring *ring = &mhi_cmd->ring;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1520,7 +1518,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl,
int chan)
{
- struct mhi_tre *dev_rp, *local_rp;
+ struct mhi_ring_element *dev_rp, *local_rp;
struct mhi_ring *ev_ring;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
unsigned long flags;
@@ -1533,7 +1531,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl,
/* mark all stale events related to channel as STALE event */
spin_lock_irqsave(&mhi_event->lock, flags);
- ptr = er_ctxt->rp;
+ ptr = le64_to_cpu(er_ctxt->rp);
if (!is_valid_ring_ptr(ev_ring, ptr)) {
dev_err(&mhi_cntrl->mhi_dev->dev,
"Event ring rp points outside of the event ring\n");
diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index b79895810c52..9527b7d63840 100644
--- a/drivers/bus/mhi/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -327,6 +327,7 @@ static const struct mhi_pci_dev_info mhi_quectel_em1xx_info = {
.config = &modem_quectel_em1xx_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
+ .mru_default = 32768,
.sideband_wake = true,
};
diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/host/pm.c
index 4aae0baea008..3d90b8ecd3d9 100644
--- a/drivers/bus/mhi/core/pm.c
+++ b/drivers/bus/mhi/host/pm.c
@@ -131,11 +131,10 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, enum mhi_state state)
{
if (state == MHI_STATE_RESET) {
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1);
+ MHICTRL_RESET_MASK, 1);
} else {
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_MHISTATE_MASK,
- MHICTRL_MHISTATE_SHIFT, state);
+ MHICTRL_MHISTATE_MASK, state);
}
}
@@ -167,16 +166,14 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
/* Wait for RESET to be cleared and READY bit to be set by the device */
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
- interval_us);
+ MHICTRL_RESET_MASK, 0, interval_us);
if (ret) {
dev_err(dev, "Device failed to clear MHI Reset\n");
return ret;
}
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
- MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT, 1,
- interval_us);
+ MHISTATUS_READY_MASK, 1, interval_us);
if (ret) {
dev_err(dev, "Device failed to enter MHI Ready\n");
return ret;
@@ -218,7 +215,7 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
continue;
ring->wp = ring->base + ring->len - ring->el_size;
- *ring->ctxt_wp = ring->iommu_base + ring->len - ring->el_size;
+ *ring->ctxt_wp = cpu_to_le64(ring->iommu_base + ring->len - ring->el_size);
/* Update all cores */
smp_wmb();
@@ -420,7 +417,7 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
continue;
ring->wp = ring->base + ring->len - ring->el_size;
- *ring->ctxt_wp = ring->iommu_base + ring->len - ring->el_size;
+ *ring->ctxt_wp = cpu_to_le64(ring->iommu_base + ring->len - ring->el_size);
/* Update to all cores */
smp_wmb();
@@ -470,8 +467,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
/* Wait for the reset bit to be cleared by the device */
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
- 25000);
+ MHICTRL_RESET_MASK, 0, 25000);
if (ret)
dev_err(dev, "Device failed to clear MHI Reset\n");
@@ -545,7 +541,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
- TO_MHI_STATE_STR(mhi_cntrl->dev_state));
+ mhi_state_str(mhi_cntrl->dev_state));
mutex_unlock(&mhi_cntrl->pm_mutex);
}
@@ -602,7 +598,6 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
mhi_cntrl->regs,
MHICTRL,
MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT,
&in_reset) ||
!in_reset, timeout);
if (!ret || in_reset) {
@@ -689,7 +684,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
exit_sys_error_transition:
dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
- TO_MHI_STATE_STR(mhi_cntrl->dev_state));
+ mhi_state_str(mhi_cntrl->dev_state));
mutex_unlock(&mhi_cntrl->pm_mutex);
}
@@ -864,7 +859,7 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
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",
- TO_MHI_STATE_STR(mhi_cntrl->dev_state),
+ mhi_state_str(mhi_cntrl->dev_state),
to_mhi_pm_state_str(mhi_cntrl->pm_state));
return -EIO;
}
@@ -890,7 +885,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
dev_dbg(dev, "Entered with PM state: %s, MHI state: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
- TO_MHI_STATE_STR(mhi_cntrl->dev_state));
+ mhi_state_str(mhi_cntrl->dev_state));
if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
return 0;
@@ -900,7 +895,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) {
dev_warn(dev, "Resuming from non M3 state (%s)\n",
- TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)));
+ mhi_state_str(mhi_get_mhi_state(mhi_cntrl)));
if (!force)
return -EINVAL;
}
@@ -937,7 +932,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev,
"Did not enter M0 state, MHI state: %s, PM state: %s\n",
- TO_MHI_STATE_STR(mhi_cntrl->dev_state),
+ mhi_state_str(mhi_cntrl->dev_state),
to_mhi_pm_state_str(mhi_cntrl->pm_state));
return -EIO;
}
@@ -1088,13 +1083,12 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
state = mhi_get_mhi_state(mhi_cntrl);
dev_dbg(dev, "Attempting power on with EE: %s, state: %s\n",
- TO_MHI_EXEC_STR(current_ee), TO_MHI_STATE_STR(state));
+ TO_MHI_EXEC_STR(current_ee), mhi_state_str(state));
if (state == MHI_STATE_SYS_ERR) {
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
- interval_us);
+ MHICTRL_RESET_MASK, 0, interval_us);
if (ret) {
dev_info(dev, "Failed to reset MHI due to syserr state\n");
goto error_exit;
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index cce2af5df7b4..d5f943938427 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -60,7 +60,7 @@ struct bsr_dev {
};
static unsigned total_bsr_devs;
-static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
+static LIST_HEAD(bsr_devs);
static struct class *bsr_class;
static int bsr_major;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 563dfae3b8da..ee71376f174b 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -16,6 +16,7 @@
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/init.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
@@ -120,22 +121,6 @@ static struct hpets *hpets;
#define HPET_PERIODIC 0x0004
#define HPET_SHARED_IRQ 0x0008
-
-#ifndef readq
-static inline unsigned long long readq(void __iomem *addr)
-{
- return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL);
-}
-#endif
-
-#ifndef writeq
-static inline void writeq(unsigned long long v, void __iomem *addr)
-{
- writel(v & 0xffffffff, addr);
- writel(v >> 32, addr + 4);
-}
-#endif
-
static irqreturn_t hpet_interrupt(int irq, void *data)
{
struct hpet_dev *devp;
@@ -268,9 +253,9 @@ static int hpet_open(struct inode *inode, struct file *file)
for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
for (i = 0; i < hpetp->hp_ntimer; i++)
- if (hpetp->hp_dev[i].hd_flags & HPET_OPEN)
+ if (hpetp->hp_dev[i].hd_flags & HPET_OPEN) {
continue;
- else {
+ } else {
devp = &hpetp->hp_dev[i];
break;
}
@@ -317,9 +302,9 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
devp->hd_irqdata = 0;
spin_unlock_irq(&hpet_lock);
- if (data)
+ if (data) {
break;
- else if (file->f_flags & O_NONBLOCK) {
+ } else if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
goto out;
} else if (signal_pending(current)) {
@@ -982,7 +967,8 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data)
break;
irq = acpi_register_gsi(NULL, irqp->interrupts[i],
- irqp->triggering, irqp->polarity);
+ irqp->triggering,
+ irqp->polarity);
if (irq < 0)
return AE_ERROR;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e3c430539a17..9fa3c76a267f 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -2245,7 +2245,7 @@ static struct virtio_driver virtio_rproc_serial = {
.remove = virtcons_remove,
};
-static int __init init(void)
+static int __init virtio_console_init(void)
{
int err;
@@ -2280,7 +2280,7 @@ free:
return err;
}
-static void __exit fini(void)
+static void __exit virtio_console_fini(void)
{
reclaim_dma_bufs();
@@ -2290,8 +2290,8 @@ static void __exit fini(void)
class_destroy(pdrvdata.class);
debugfs_remove_recursive(pdrvdata.debugfs_dir);
}
-module_init(init);
-module_exit(fini);
+module_init(virtio_console_init);
+module_exit(virtio_console_fini);
MODULE_DESCRIPTION("Virtio console driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c
index 02225eb19cf6..619f3a30ec55 100644
--- a/drivers/char/xilinx_hwicap/fifo_icap.c
+++ b/drivers/char/xilinx_hwicap/fifo_icap.c
@@ -111,7 +111,7 @@ static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata)
}
/**
- * fifo_icap_set_read_size - Set the the size register.
+ * fifo_icap_set_read_size - Set the size register.
* @drvdata: a pointer to the drvdata.
* @data: the size of the following read transaction, in words.
**/
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 067396bedf22..74a4928aea1d 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -241,7 +241,7 @@ static int hwicap_command_desync(struct hwicap_drvdata *drvdata)
buffer[index++] = XHI_NOOP_PACKET;
/*
- * Write the data to the FIFO and intiate the transfer of data present
+ * Write the data to the FIFO and initiate the transfer of data present
* in the FIFO to the ICAP device.
*/
return drvdata->config->set_configuration(drvdata,
@@ -297,7 +297,7 @@ static int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata,
buffer[index++] = XHI_NOOP_PACKET;
/*
- * Write the data to the FIFO and intiate the transfer of data present
+ * Write the data to the FIFO and initiate the transfer of data present
* in the FIFO to the ICAP device.
*/
status = drvdata->config->set_configuration(drvdata,
@@ -384,7 +384,7 @@ hwicap_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
drvdata->read_buffer + bytes_to_read,
4 - bytes_to_read);
} else {
- /* Get new data from the ICAP, and return was was requested. */
+ /* Get new data from the ICAP, and return what was requested. */
kbuf = (u32 *) get_zeroed_page(GFP_KERNEL);
if (!kbuf) {
status = -ENOMEM;
diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c
index 937a69ce0977..728dc02156c8 100644
--- a/drivers/comedi/drivers/das16.c
+++ b/drivers/comedi/drivers/das16.c
@@ -961,7 +961,7 @@ static const struct comedi_lrange *das16_ai_range(struct comedi_device *dev,
/* allocate single-range range table */
lrange = comedi_alloc_spriv(s,
- sizeof(*lrange) + sizeof(*krange));
+ struct_size(lrange, range, 1));
if (!lrange)
return &range_unknown;
@@ -995,7 +995,7 @@ static const struct comedi_lrange *das16_ao_range(struct comedi_device *dev,
/* allocate single-range range table */
lrange = comedi_alloc_spriv(s,
- sizeof(*lrange) + sizeof(*krange));
+ struct_size(lrange, range, 1));
if (!lrange)
return &range_unknown;
diff --git a/drivers/comedi/drivers/ni_routes.c b/drivers/comedi/drivers/ni_routes.c
index f24eeb464eba..295a3a9ee0c9 100644
--- a/drivers/comedi/drivers/ni_routes.c
+++ b/drivers/comedi/drivers/ni_routes.c
@@ -56,8 +56,7 @@ static const u8 *ni_find_route_values(const char *device_family)
int i;
for (i = 0; ni_all_route_values[i]; ++i) {
- if (memcmp(ni_all_route_values[i]->family, device_family,
- strnlen(device_family, 30)) == 0) {
+ if (!strcmp(ni_all_route_values[i]->family, device_family)) {
rv = &ni_all_route_values[i]->register_values[0][0];
break;
}
@@ -75,8 +74,7 @@ ni_find_valid_routes(const char *board_name)
int i;
for (i = 0; ni_device_routes_list[i]; ++i) {
- if (memcmp(ni_device_routes_list[i]->device, board_name,
- strnlen(board_name, 30)) == 0) {
+ if (!strcmp(ni_device_routes_list[i]->device, board_name)) {
dr = ni_device_routes_list[i];
break;
}
diff --git a/drivers/comedi/drivers/pcm3724.c b/drivers/comedi/drivers/pcm3724.c
index e4103f9eeced..ca8bef54dacc 100644
--- a/drivers/comedi/drivers/pcm3724.c
+++ b/drivers/comedi/drivers/pcm3724.c
@@ -93,7 +93,6 @@ static void do_3724_config(struct comedi_device *dev,
unsigned long port_8255_cfg;
config = I8255_CTRL_CW;
- buffer_config = 0;
/* 1 in io_bits indicates output, 1 in config indicates input */
if (!(s->io_bits & 0x0000ff))
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index 3dcdb681c4e4..5edd155f1911 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -14,7 +14,7 @@ if COUNTER
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
- depends on PC104 && X86
+ depends on (PC104 && X86) || COMPILE_TEST
select ISA_BUS_API
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
index b7c62f957a6a..69d340be9c93 100644
--- a/drivers/counter/counter-chrdev.c
+++ b/drivers/counter/counter-chrdev.c
@@ -477,6 +477,8 @@ static int counter_get_data(struct counter_device *const counter,
case COUNTER_SCOPE_COUNT:
ret = comp->count_u8_read(counter, parent, &value_u8);
break;
+ default:
+ return -EINVAL;
}
*value = value_u8;
return ret;
@@ -496,6 +498,8 @@ static int counter_get_data(struct counter_device *const counter,
case COUNTER_SCOPE_COUNT:
ret = comp->count_u32_read(counter, parent, &value_u32);
break;
+ default:
+ return -EINVAL;
}
*value = value_u32;
return ret;
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 869894b74741..938651f9e9e0 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -22,6 +22,8 @@
#include "counter-chrdev.h"
#include "counter-sysfs.h"
+#define COUNTER_NAME "counter"
+
/* Provides a unique ID for each counter device */
static DEFINE_IDA(counter_ida);
@@ -113,8 +115,15 @@ struct counter_device *counter_alloc(size_t sizeof_priv)
device_initialize(dev);
+ err = dev_set_name(dev, COUNTER_NAME "%d", dev->id);
+ if (err)
+ goto err_dev_set_name;
+
return counter;
+err_dev_set_name:
+
+ counter_chrdev_remove(counter);
err_chrdev_add:
ida_free(&counter_ida, dev->id);
@@ -247,7 +256,8 @@ static int __init counter_init(void)
if (err < 0)
return err;
- err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+ err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX,
+ COUNTER_NAME);
if (err < 0)
goto err_unregister_bus;
diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
index 9e99702470c2..3b13f56bbb11 100644
--- a/drivers/counter/interrupt-cnt.c
+++ b/drivers/counter/interrupt-cnt.c
@@ -26,10 +26,13 @@ struct interrupt_cnt_priv {
static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
{
- struct interrupt_cnt_priv *priv = dev_id;
+ struct counter_device *counter = dev_id;
+ struct interrupt_cnt_priv *priv = counter_priv(counter);
atomic_inc(&priv->count);
+ counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);
+
return IRQ_HANDLED;
}
@@ -209,7 +212,7 @@ static int interrupt_cnt_probe(struct platform_device *pdev)
irq_set_status_flags(priv->irq, IRQ_NOAUTOEN);
ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
- dev_name(dev), priv);
+ dev_name(dev), counter);
if (ret)
return ret;
diff --git a/drivers/dio/dio.c b/drivers/dio/dio.c
index 4c06c93c93d3..005a82f671c3 100644
--- a/drivers/dio/dio.c
+++ b/drivers/dio/dio.c
@@ -2,27 +2,27 @@
/* Code to support devices on the DIO and DIO-II bus
* Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
* Copyright (C) 2004 Jochen Friedrich <jochen@scram.de>
- *
+ *
* This code has basically these routines at the moment:
* int dio_find(u_int deviceid)
* Search the list of DIO devices and return the select code
* of the next unconfigured device found that matches the given device ID.
* Note that the deviceid parameter should be the encoded ID.
- * This means that framebuffers should pass it as
+ * This means that framebuffers should pass it as
* DIO_ENCODE_ID(DIO_ID_FBUFFER,DIO_ID2_TOPCAT)
* (or whatever); everybody else just uses DIO_ID_FOOBAR.
* unsigned long dio_scodetophysaddr(int scode)
* Return the physical address corresponding to the given select code.
* int dio_scodetoipl(int scode)
- * Every DIO card has a fixed interrupt priority level. This function
+ * Every DIO card has a fixed interrupt priority level. This function
* returns it, whatever it is.
* const char *dio_scodetoname(int scode)
- * Return a character string describing this board [might be "" if
+ * Return a character string describing this board [might be "" if
* not CONFIG_DIO_CONSTANTS]
* void dio_config_board(int scode) mark board as configured in the list
* void dio_unconfig_board(int scode) mark board as no longer configured
*
- * This file is based on the way the Amiga port handles Zorro II cards,
+ * This file is based on the way the Amiga port handles Zorro II cards,
* although we aren't so complicated...
*/
#include <linux/module.h>
@@ -33,7 +33,7 @@
#include <linux/dio.h>
#include <linux/slab.h> /* kmalloc() */
#include <linux/uaccess.h>
-#include <asm/io.h> /* readb() */
+#include <linux/io.h> /* readb() */
struct dio_bus dio_bus = {
.resources = {
@@ -52,38 +52,36 @@ struct dio_bus dio_bus = {
/* We associate each numeric ID with an appropriate descriptive string
* using a constant array of these structs.
* FIXME: we should be able to arrange to throw away most of the strings
- * using the initdata stuff. Then we wouldn't need to worry about
+ * using the initdata stuff. Then we wouldn't need to worry about
* carrying them around...
- * I think we do this by copying them into newly kmalloc()ed memory and
+ * I think we do this by copying them into newly kmalloc()ed memory and
* marking the names[] array as .initdata ?
*/
-struct dioname
-{
- int id;
- const char *name;
+struct dioname {
+ int id;
+ const char *name;
};
/* useful macro */
#define DIONAME(x) { DIO_ID_##x, DIO_DESC_##x }
-#define DIOFBNAME(x) { DIO_ENCODE_ID( DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x }
-
-static struct dioname names[] =
-{
- DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM),
- DIONAME(DCM), DIONAME(DCMREM),
- DIONAME(LAN),
- DIONAME(FHPIB), DIONAME(NHPIB),
- DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3),
- DIONAME(FBUFFER),
- DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM),
- DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3),
- DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7),
- DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11),
- DIONAME(MISC12), DIONAME(MISC13),
- DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE),
- DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE),
- DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION),
- DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS)
+#define DIOFBNAME(x) { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x }
+
+static struct dioname names[] = {
+ DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM),
+ DIONAME(DCM), DIONAME(DCMREM),
+ DIONAME(LAN),
+ DIONAME(FHPIB), DIONAME(NHPIB),
+ DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3),
+ DIONAME(FBUFFER),
+ DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM),
+ DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3),
+ DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7),
+ DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11),
+ DIONAME(MISC12), DIONAME(MISC13),
+ DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE),
+ DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE),
+ DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION),
+ DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS)
};
#undef DIONAME
@@ -94,13 +92,14 @@ static const char unknowndioname[]
static const char *dio_getname(int id)
{
- /* return pointer to a constant string describing the board with given ID */
+ /* return pointer to a constant string describing the board with given ID */
unsigned int i;
+
for (i = 0; i < ARRAY_SIZE(names); i++)
- if (names[i].id == id)
- return names[i].name;
+ if (names[i].id == id)
+ return names[i].name;
- return unknowndioname;
+ return unknowndioname;
}
#else
@@ -122,10 +121,10 @@ int __init dio_find(int deviceid)
void *va;
unsigned long pa;
- if (DIO_SCINHOLE(scode))
- continue;
+ if (DIO_SCINHOLE(scode))
+ continue;
- pa = dio_scodetophysaddr(scode);
+ pa = dio_scodetophysaddr(scode);
if (!pa)
continue;
@@ -139,15 +138,15 @@ int __init dio_find(int deviceid)
(unsigned char *)va + DIO_IDOFF, 1)) {
if (scode >= DIOII_SCBASE)
iounmap(va);
- continue; /* no board present at that select code */
+ continue; /* no board present at that select code */
}
prid = DIO_ID(va);
- if (DIO_NEEDSSECID(prid)) {
- secid = DIO_SECID(va);
- id = DIO_ENCODE_ID(prid, secid);
- } else
+ if (DIO_NEEDSSECID(prid)) {
+ secid = DIO_SECID(va);
+ id = DIO_ENCODE_ID(prid, secid);
+ } else
id = prid;
if (id == deviceid) {
@@ -175,7 +174,7 @@ static int __init dio_init(void)
printk(KERN_INFO "Scanning for DIO devices...\n");
- /* Initialize the DIO bus */
+ /* Initialize the DIO bus */
INIT_LIST_HEAD(&dio_bus.devices);
dev_set_name(&dio_bus.dev, "dio");
error = device_register(&dio_bus.dev);
@@ -190,14 +189,13 @@ static int __init dio_init(void)
request_resource(&iomem_resource, &dio_bus.resources[i]);
/* Register all devices */
- for (scode = 0; scode < DIO_SCMAX; ++scode)
- {
- u_char prid, secid = 0; /* primary, secondary ID bytes */
- u_char *va;
+ for (scode = 0; scode < DIO_SCMAX; ++scode) {
+ u_char prid, secid = 0; /* primary, secondary ID bytes */
+ u_char *va;
unsigned long pa;
-
- if (DIO_SCINHOLE(scode))
- continue;
+
+ if (DIO_SCINHOLE(scode))
+ continue;
pa = dio_scodetophysaddr(scode);
@@ -213,10 +211,10 @@ static int __init dio_init(void)
(unsigned char *)va + DIO_IDOFF, 1)) {
if (scode >= DIOII_SCBASE)
iounmap(va);
- continue; /* no board present at that select code */
+ continue; /* no board present at that select code */
}
- /* Found a board, allocate it an entry in the list */
+ /* Found a board, allocate it an entry in the list */
dev = kzalloc(sizeof(struct dio_dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -229,19 +227,19 @@ static int __init dio_init(void)
dev->resource.end = pa + DIO_SIZE(scode, va);
dev_set_name(&dev->dev, "%02x", scode);
- /* read the ID byte(s) and encode if necessary. */
+ /* read the ID byte(s) and encode if necessary. */
prid = DIO_ID(va);
- if (DIO_NEEDSSECID(prid)) {
- secid = DIO_SECID(va);
- dev->id = DIO_ENCODE_ID(prid, secid);
- } else
- dev->id = prid;
+ if (DIO_NEEDSSECID(prid)) {
+ secid = DIO_SECID(va);
+ dev->id = DIO_ENCODE_ID(prid, secid);
+ } else
+ dev->id = prid;
- dev->ipl = DIO_IPL(va);
- strcpy(dev->name,dio_getname(dev->id));
+ dev->ipl = DIO_IPL(va);
+ strcpy(dev->name, dio_getname(dev->id));
printk(KERN_INFO "select code %3d: ipl %d: ID %02X", dev->scode, dev->ipl, prid);
- if (DIO_NEEDSSECID(prid))
+ if (DIO_NEEDSSECID(prid))
printk(":%02X", secid);
printk(": %s\n", dev->name);
@@ -256,7 +254,7 @@ static int __init dio_init(void)
error = dio_create_sysfs_dev_files(dev);
if (error)
dev_err(&dev->dev, "Error creating sysfs files\n");
- }
+ }
return 0;
}
@@ -267,12 +265,12 @@ subsys_initcall(dio_init);
*/
unsigned long dio_scodetophysaddr(int scode)
{
- if (scode >= DIOII_SCBASE) {
- return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE);
- } else if (scode > DIO_SCMAX || scode < 0)
- return 0;
- else if (DIO_SCINHOLE(scode))
- return 0;
-
- return (DIO_BASE + scode * DIO_DEVSIZE);
+ if (scode >= DIOII_SCBASE)
+ return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE);
+ else if (scode > DIO_SCMAX || scode < 0)
+ return 0;
+ else if (DIO_SCINHOLE(scode))
+ return 0;
+
+ return (DIO_BASE + scode * DIO_DEVSIZE);
}
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig
index 931544c9f63d..983e07dc022e 100644
--- a/drivers/firmware/google/Kconfig
+++ b/drivers/firmware/google/Kconfig
@@ -21,7 +21,7 @@ config GOOGLE_SMI
config GOOGLE_COREBOOT_TABLE
tristate "Coreboot Table Access"
- depends on ACPI || OF
+ depends on HAS_IOMEM && (ACPI || OF)
help
This option enables the coreboot_table module, which provides other
firmware modules access to the coreboot table. The coreboot table
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index 29c0a616b317..8177a0fae11d 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -306,6 +306,7 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
break;
case COMMAND_RSU_RETRY:
case COMMAND_RSU_MAX_RETRY:
+ case COMMAND_FIRMWARE_VERSION:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1;
break;
@@ -422,6 +423,11 @@ static int svc_normal_to_secure_thread(void *data)
a1 = 0;
a2 = 0;
break;
+ case COMMAND_FIRMWARE_VERSION:
+ a0 = INTEL_SIP_SMC_FIRMWARE_VERSION;
+ a1 = 0;
+ a2 = 0;
+ break;
default:
pr_warn("it shouldn't happen\n");
break;
@@ -477,7 +483,7 @@ 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 = NULL;
+ cbdata->kaddr1 = &res.a1;
cbdata->kaddr2 = NULL;
cbdata->kaddr3 = NULL;
pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
@@ -491,7 +497,8 @@ static int svc_normal_to_secure_thread(void *data)
*/
if ((pdata->command == COMMAND_RSU_RETRY) ||
(pdata->command == COMMAND_RSU_MAX_RETRY) ||
- (pdata->command == COMMAND_RSU_NOTIFY)) {
+ (pdata->command == COMMAND_RSU_NOTIFY) ||
+ (pdata->command == COMMAND_FIRMWARE_VERSION)) {
cbdata->status =
BIT(SVC_STATUS_NO_SUPPORT);
cbdata->kaddr1 = NULL;
diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c
index 76c4abc42a30..bda8712bfd8c 100644
--- a/drivers/firmware/sysfb_simplefb.c
+++ b/drivers/firmware/sysfb_simplefb.c
@@ -113,16 +113,21 @@ __init int sysfb_create_simplefb(const struct screen_info *si,
sysfb_apply_efi_quirks(pd);
ret = platform_device_add_resources(pd, &res, 1);
- if (ret) {
- platform_device_put(pd);
- return ret;
- }
+ if (ret)
+ goto err_put_device;
ret = platform_device_add_data(pd, mode, sizeof(*mode));
- if (ret) {
- platform_device_put(pd);
- return ret;
- }
+ if (ret)
+ goto err_put_device;
+
+ ret = platform_device_add(pd);
+ if (ret)
+ goto err_put_device;
+
+ return 0;
+
+err_put_device:
+ platform_device_put(pd);
- return platform_device_add(pd);
+ return ret;
}
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index 5e5b0bb2e4e0..f21ece56695e 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -42,6 +42,16 @@ static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER);
static struct platform_device *em_dev;
/**
+ * struct zynqmp_devinfo - Structure for Zynqmp device instance
+ * @dev: Device Pointer
+ * @feature_conf_id: Feature conf id
+ */
+struct zynqmp_devinfo {
+ struct device *dev;
+ u32 feature_conf_id;
+};
+
+/**
* struct pm_api_feature_data - PM API Feature data
* @pm_api_id: PM API Id, used as key to index into hashmap
* @feature_status: status of PM API feature: valid, invalid
@@ -1183,6 +1193,33 @@ int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)
}
/**
+ * zynqmp_pm_set_feature_config - PM call to request IOCTL for feature config
+ * @id: The config ID of the feature to be configured
+ * @value: The config value of the feature to be configured
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_FEATURE_CONFIG,
+ id, value, NULL);
+}
+
+/**
+ * zynqmp_pm_get_feature_config - PM call to get value of configured feature
+ * @id: The config id of the feature to be queried
+ * @payload: Returned value array
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_get_feature_config(enum pm_feature_config_id id,
+ u32 *payload)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_GET_FEATURE_CONFIG,
+ id, 0, payload);
+}
+
+/**
* struct zynqmp_pm_shutdown_scope - Struct for shutdown scope
* @subtype: Shutdown subtype
* @name: Matching string for scope argument
@@ -1450,6 +1487,78 @@ static DEVICE_ATTR_RW(pggs1);
static DEVICE_ATTR_RW(pggs2);
static DEVICE_ATTR_RW(pggs3);
+static ssize_t feature_config_id_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);
+
+ return sysfs_emit(buf, "%d\n", devinfo->feature_conf_id);
+}
+
+static ssize_t feature_config_id_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 config_id;
+ int ret;
+ struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);
+
+ if (!buf)
+ return -EINVAL;
+
+ ret = kstrtou32(buf, 10, &config_id);
+ if (ret)
+ return ret;
+
+ devinfo->feature_conf_id = config_id;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(feature_config_id);
+
+static ssize_t feature_config_value_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);
+
+ ret = zynqmp_pm_get_feature_config(devinfo->feature_conf_id,
+ ret_payload);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", ret_payload[1]);
+}
+
+static ssize_t feature_config_value_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 value;
+ int ret;
+ struct zynqmp_devinfo *devinfo = dev_get_drvdata(device);
+
+ if (!buf)
+ return -EINVAL;
+
+ ret = kstrtou32(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ ret = zynqmp_pm_set_feature_config(devinfo->feature_conf_id,
+ value);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(feature_config_value);
+
static struct attribute *zynqmp_firmware_attrs[] = {
&dev_attr_ggs0.attr,
&dev_attr_ggs1.attr,
@@ -1461,6 +1570,8 @@ static struct attribute *zynqmp_firmware_attrs[] = {
&dev_attr_pggs3.attr,
&dev_attr_shutdown_scope.attr,
&dev_attr_health_status.attr,
+ &dev_attr_feature_config_id.attr,
+ &dev_attr_feature_config_value.attr,
NULL,
};
@@ -1470,6 +1581,7 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np;
+ struct zynqmp_devinfo *devinfo;
int ret;
np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
@@ -1486,6 +1598,14 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
if (ret)
return ret;
+ devinfo = devm_kzalloc(dev, sizeof(*devinfo), GFP_KERNEL);
+ if (!devinfo)
+ return -ENOMEM;
+
+ devinfo->dev = dev;
+
+ platform_set_drvdata(pdev, devinfo);
+
/* Check PM API version number */
ret = zynqmp_pm_get_api_version(&pm_api_version);
if (ret)
diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c
index 4d68719e608f..717ac9715970 100644
--- a/drivers/fpga/dfl-pci.c
+++ b/drivers/fpga/dfl-pci.c
@@ -15,6 +15,7 @@
*/
#include <linux/pci.h>
+#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -354,16 +355,10 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
pci_set_master(pcidev);
- if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) {
- ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64));
- if (ret)
- goto disable_error_report_exit;
- } else if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(32))) {
- ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
- if (ret)
- goto disable_error_report_exit;
- } else {
- ret = -EIO;
+ ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64));
+ if (ret)
+ ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32));
+ if (ret) {
dev_err(&pcidev->dev, "No suitable DMA support available.\n");
goto disable_error_report_exit;
}
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 59ddc9fd5bca..3a7b78e36701 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -24,9 +24,6 @@
#include "fsi-master.h"
-#define CREATE_TRACE_POINTS
-#include <trace/events/fsi.h>
-
#define FSI_SLAVE_CONF_NEXT_MASK GENMASK(31, 31)
#define FSI_SLAVE_CONF_SLOTS_MASK GENMASK(23, 16)
#define FSI_SLAVE_CONF_SLOTS_SHIFT 16
@@ -95,6 +92,9 @@ struct fsi_slave {
u8 t_echo_delay;
};
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi.h>
+
#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
@@ -524,6 +524,8 @@ static int fsi_slave_scan(struct fsi_slave *slave)
dev->addr = engine_addr;
dev->size = slots * engine_page_size;
+ trace_fsi_dev_init(dev);
+
dev_dbg(&slave->dev,
"engine[%i]: type %x, version %x, addr %x size %x\n",
dev->unit, dev->engine_type, version,
@@ -1006,6 +1008,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
crc = crc4(0, cfam_id, 32);
if (crc) {
+ trace_fsi_slave_invalid_cfam(master, link, cfam_id);
dev_warn(&master->dev, "slave %02x:%02x invalid cfam id CRC!\n",
link, id);
return -EIO;
@@ -1080,6 +1083,8 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
if (rc)
goto err_free;
+ trace_fsi_slave_init(slave);
+
/* Create chardev for userspace access */
cdev_init(&slave->cdev, &cfam_fops);
rc = cdev_device_add(&slave->cdev, &slave->dev);
diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 8606e55c1721..7cec1772820d 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -449,11 +449,13 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att
{
struct fsi_master_aspeed *aspeed = dev_get_drvdata(dev);
+ trace_fsi_master_aspeed_cfam_reset(true);
mutex_lock(&aspeed->lock);
gpiod_set_value(aspeed->cfam_reset_gpio, 1);
usleep_range(900, 1000);
gpiod_set_value(aspeed->cfam_reset_gpio, 0);
mutex_unlock(&aspeed->lock);
+ trace_fsi_master_aspeed_cfam_reset(false);
return count;
}
@@ -542,25 +544,28 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
return rc;
}
- aspeed = devm_kzalloc(&pdev->dev, sizeof(*aspeed), GFP_KERNEL);
+ aspeed = kzalloc(sizeof(*aspeed), GFP_KERNEL);
if (!aspeed)
return -ENOMEM;
aspeed->dev = &pdev->dev;
aspeed->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(aspeed->base))
- return PTR_ERR(aspeed->base);
+ if (IS_ERR(aspeed->base)) {
+ rc = PTR_ERR(aspeed->base);
+ goto err_free_aspeed;
+ }
aspeed->clk = devm_clk_get(aspeed->dev, NULL);
if (IS_ERR(aspeed->clk)) {
dev_err(aspeed->dev, "couldn't get clock\n");
- return PTR_ERR(aspeed->clk);
+ rc = PTR_ERR(aspeed->clk);
+ goto err_free_aspeed;
}
rc = clk_prepare_enable(aspeed->clk);
if (rc) {
dev_err(aspeed->dev, "couldn't enable clock\n");
- return rc;
+ goto err_free_aspeed;
}
rc = setup_cfam_reset(aspeed);
@@ -595,7 +600,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
rc = opb_readl(aspeed, ctrl_base + FSI_MVER, &raw);
if (rc) {
dev_err(&pdev->dev, "failed to read hub version\n");
- return rc;
+ goto err_release;
}
reg = be32_to_cpu(raw);
@@ -634,6 +639,8 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
err_release:
clk_disable_unprepare(aspeed->clk);
+err_free_aspeed:
+ kfree(aspeed);
return rc;
}
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index 7eaab1be0aa4..c9cc75fbdfb9 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -451,6 +451,14 @@ static int occ_trigger_attn(struct occ *occ)
return rc;
}
+static bool fsi_occ_response_not_ready(struct occ_response *resp, u8 seq_no,
+ u8 cmd_type)
+{
+ return resp->return_status == OCC_RESP_CMD_IN_PRG ||
+ resp->return_status == OCC_RESP_CRIT_INIT ||
+ resp->seq_no != seq_no || resp->cmd_type != cmd_type;
+}
+
int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
void *response, size_t *resp_len)
{
@@ -461,10 +469,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
struct occ_response *resp = response;
size_t user_resp_len = *resp_len;
u8 seq_no;
+ u8 cmd_type;
u16 checksum = 0;
u16 resp_data_length;
const u8 *byte_request = (const u8 *)request;
- unsigned long start;
+ unsigned long end;
int rc;
size_t i;
@@ -478,6 +487,8 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
return -EINVAL;
}
+ cmd_type = byte_request[1];
+
/* Checksum the request, ignoring first byte (sequence number). */
for (i = 1; i < req_len - 2; ++i)
checksum += byte_request[i];
@@ -509,51 +520,61 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
if (rc)
goto done;
- /* Read occ response header */
- start = jiffies;
- do {
+ end = jiffies + timeout;
+ while (true) {
+ /* Read occ response header */
rc = occ_getsram(occ, 0, resp, 8);
if (rc)
goto done;
- if (resp->return_status == OCC_RESP_CMD_IN_PRG ||
- resp->return_status == OCC_RESP_CRIT_INIT ||
- resp->seq_no != seq_no) {
- rc = -ETIMEDOUT;
-
- if (time_after(jiffies, start + timeout)) {
- dev_err(occ->dev, "resp timeout status=%02x "
- "resp seq_no=%d our seq_no=%d\n",
+ if (fsi_occ_response_not_ready(resp, seq_no, cmd_type)) {
+ if (time_after(jiffies, end)) {
+ dev_err(occ->dev,
+ "resp timeout status=%02x seq=%d cmd=%d, our seq=%d cmd=%d\n",
resp->return_status, resp->seq_no,
- seq_no);
+ resp->cmd_type, seq_no, cmd_type);
+ rc = -ETIMEDOUT;
goto done;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait_time);
- }
- } while (rc);
-
- /* Extract size of response data */
- resp_data_length = get_unaligned_be16(&resp->data_length);
+ } else {
+ /* Extract size of response data */
+ resp_data_length =
+ get_unaligned_be16(&resp->data_length);
+
+ /*
+ * Message size is data length + 5 bytes header + 2
+ * bytes checksum
+ */
+ if ((resp_data_length + 7) > user_resp_len) {
+ rc = -EMSGSIZE;
+ goto done;
+ }
- /* Message size is data length + 5 bytes header + 2 bytes checksum */
- if ((resp_data_length + 7) > user_resp_len) {
- rc = -EMSGSIZE;
- goto done;
+ /*
+ * Get the entire response including the header again,
+ * in case it changed
+ */
+ if (resp_data_length > 1) {
+ rc = occ_getsram(occ, 0, resp,
+ resp_data_length + 7);
+ if (rc)
+ goto done;
+
+ if (!fsi_occ_response_not_ready(resp, seq_no,
+ cmd_type))
+ break;
+ } else {
+ break;
+ }
+ }
}
dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n",
resp->return_status, resp_data_length);
- /* Grab the rest */
- if (resp_data_length > 1) {
- /* already got 3 bytes resp, also need 2 bytes checksum */
- rc = occ_getsram(occ, 8, &resp->data[3], resp_data_length - 1);
- if (rc)
- goto done;
- }
-
occ->client_response_size = resp_data_length + 7;
rc = occ_verify_checksum(occ, resp, resp_data_length);
@@ -598,7 +619,11 @@ static int occ_probe(struct platform_device *pdev)
occ->version = (uintptr_t)of_device_get_match_data(dev);
occ->dev = dev;
occ->sbefifo = dev->parent;
- occ->sequence_number = 1;
+ /*
+ * Quickly derive a pseudo-random number from jiffies so that
+ * re-probing the driver doesn't accidentally overlap sequence numbers.
+ */
+ occ->sequence_number = (u8)((jiffies % 0xff) + 1);
mutex_init(&occ->occ_lock);
if (dev->of_node) {
diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index 52328adef643..f52a912cdf16 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -32,6 +32,8 @@
#include <linux/vmalloc.h>
#include <linux/mm.h>
+#include <uapi/linux/fsi.h>
+
/*
* The SBEFIFO is a pipe-like FSI device for communicating with
* the self boot engine on POWER processors.
@@ -125,6 +127,7 @@ struct sbefifo {
bool dead;
bool async_ffdc;
bool timed_out;
+ u32 timeout_start_rsp_ms;
};
struct sbefifo_user {
@@ -133,6 +136,7 @@ struct sbefifo_user {
void *cmd_page;
void *pending_cmd;
size_t pending_len;
+ u32 read_timeout_ms;
};
static DEFINE_MUTEX(sbefifo_ffdc_mutex);
@@ -549,7 +553,7 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
dev_vdbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response));
- timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_RSP);
+ timeout = msecs_to_jiffies(sbefifo->timeout_start_rsp_ms);
for (;;) {
/* Grab FIFO status (this will handle parity errors) */
rc = sbefifo_wait(sbefifo, false, &status, timeout);
@@ -795,6 +799,7 @@ static int sbefifo_user_open(struct inode *inode, struct file *file)
return -ENOMEM;
}
mutex_init(&user->file_lock);
+ user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
return 0;
}
@@ -837,7 +842,9 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
rc = mutex_lock_interruptible(&sbefifo->lock);
if (rc)
goto bail;
+ sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
+ sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
mutex_unlock(&sbefifo->lock);
if (rc < 0)
goto bail;
@@ -927,12 +934,55 @@ static int sbefifo_user_release(struct inode *inode, struct file *file)
return 0;
}
+static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
+{
+ struct device *dev = &user->sbefifo->dev;
+ u32 timeout;
+
+ if (get_user(timeout, (__u32 __user *)argp))
+ return -EFAULT;
+
+ if (timeout == 0) {
+ user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
+ dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
+ return 0;
+ }
+
+ if (timeout < 10 || timeout > 120)
+ return -EINVAL;
+
+ user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
+
+ dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
+
+ return 0;
+}
+
+static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct sbefifo_user *user = file->private_data;
+ int rc = -ENOTTY;
+
+ if (!user)
+ return -EINVAL;
+
+ mutex_lock(&user->file_lock);
+ switch (cmd) {
+ case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
+ rc = sbefifo_read_timeout(user, (void __user *)arg);
+ break;
+ }
+ mutex_unlock(&user->file_lock);
+ return rc;
+}
+
static const struct file_operations sbefifo_fops = {
.owner = THIS_MODULE,
.open = sbefifo_user_open,
.read = sbefifo_user_read,
.write = sbefifo_user_write,
.release = sbefifo_user_release,
+ .unlocked_ioctl = sbefifo_user_ioctl,
};
static void sbefifo_free(struct device *dev)
@@ -972,6 +1022,7 @@ static int sbefifo_probe(struct device *dev)
sbefifo->fsi_dev = fsi_dev;
dev_set_drvdata(dev, sbefifo);
mutex_init(&sbefifo->lock);
+ sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
/*
* Try cleaning up the FIFO. If this fails, we still register the
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index da1486bb6a14..bcb756dc9866 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -145,7 +145,7 @@ static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value,
uint64_t addr, uint32_t *status)
{
uint64_t ind_data, ind_addr;
- int rc, retries, err = 0;
+ int rc, err;
if (value & ~XSCOM_DATA_IND_DATA)
return -EINVAL;
@@ -156,19 +156,14 @@ static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value,
if (rc || (*status & SCOM_STATUS_ANY_ERR))
return rc;
- for (retries = 0; retries < SCOM_MAX_IND_RETRIES; retries++) {
- rc = __get_scom(scom, &ind_data, addr, status);
- if (rc || (*status & SCOM_STATUS_ANY_ERR))
- return rc;
+ rc = __get_scom(scom, &ind_data, addr, status);
+ if (rc || (*status & SCOM_STATUS_ANY_ERR))
+ return rc;
- err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
- *status = err << SCOM_STATUS_PIB_RESP_SHIFT;
- if ((ind_data & XSCOM_DATA_IND_COMPLETE) || (err != SCOM_PIB_BLOCKED))
- return 0;
+ err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
+ *status = err << SCOM_STATUS_PIB_RESP_SHIFT;
- msleep(1);
- }
- return rc;
+ return 0;
}
static int put_indirect_scom_form1(struct scom_device *scom, uint64_t value,
@@ -188,7 +183,7 @@ static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value,
uint64_t addr, uint32_t *status)
{
uint64_t ind_data, ind_addr;
- int rc, retries, err = 0;
+ int rc, err;
ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | XSCOM_DATA_IND_READ;
@@ -196,21 +191,15 @@ static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value,
if (rc || (*status & SCOM_STATUS_ANY_ERR))
return rc;
- for (retries = 0; retries < SCOM_MAX_IND_RETRIES; retries++) {
- rc = __get_scom(scom, &ind_data, addr, status);
- if (rc || (*status & SCOM_STATUS_ANY_ERR))
- return rc;
-
- err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
- *status = err << SCOM_STATUS_PIB_RESP_SHIFT;
- *value = ind_data & XSCOM_DATA_IND_DATA;
+ rc = __get_scom(scom, &ind_data, addr, status);
+ if (rc || (*status & SCOM_STATUS_ANY_ERR))
+ return rc;
- if ((ind_data & XSCOM_DATA_IND_COMPLETE) || (err != SCOM_PIB_BLOCKED))
- return 0;
+ err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
+ *status = err << SCOM_STATUS_PIB_RESP_SHIFT;
+ *value = ind_data & XSCOM_DATA_IND_DATA;
- msleep(1);
- }
- return rc;
+ return 0;
}
static int raw_put_scom(struct scom_device *scom, uint64_t value,
@@ -289,7 +278,7 @@ static int put_scom(struct scom_device *scom, uint64_t value,
int rc;
rc = raw_put_scom(scom, value, addr, &status);
- if (rc == -ENODEV)
+ if (rc)
return rc;
rc = handle_fsi2pib_status(scom, status);
@@ -308,7 +297,7 @@ static int get_scom(struct scom_device *scom, uint64_t *value,
int rc;
rc = raw_get_scom(scom, value, addr, &status);
- if (rc == -ENODEV)
+ if (rc)
return rc;
rc = handle_fsi2pib_status(scom, status);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b2d313fe3fc5..45764ec3b2eb 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1356,6 +1356,18 @@ config GPIO_TIMBERDALE
help
Add support for the GPIO IP in the timberdale FPGA.
+config GPIO_TN48M_CPLD
+ tristate "Delta Networks TN48M switch CPLD GPIO driver"
+ depends on MFD_TN48M_CPLD
+ select GPIO_REGMAP
+ help
+ This enables support for the GPIOs found on the Delta
+ Networks TN48M switch Lattice CPLD. It provides 12 pins in total,
+ they are input-only or output-only type.
+
+ This driver can also be built as a module. If so, the
+ module will be called gpio-tn48m.
+
config GPIO_TPS65086
tristate "TI TPS65086 GPO"
depends on MFD_TPS65086
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 1a14e248bdbd..14352f6dfe8e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -149,6 +149,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
+obj-$(CONFIG_GPIO_TN48M_CPLD) += gpio-tn48m.o
obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o
obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o
obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o
diff --git a/drivers/gpio/gpio-tn48m.c b/drivers/gpio/gpio-tn48m.c
new file mode 100644
index 000000000000..cd4a80b22794
--- /dev/null
+++ b/drivers/gpio/gpio-tn48m.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Delta TN48M CPLD GPIO driver
+ *
+ * Copyright (C) 2021 Sartura Ltd.
+ *
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+enum tn48m_gpio_type {
+ TN48M_GP0 = 1,
+ TN48M_GPI,
+};
+
+struct tn48m_gpio_config {
+ int ngpio;
+ int ngpio_per_reg;
+ enum tn48m_gpio_type type;
+};
+
+static const struct tn48m_gpio_config tn48m_gpo_config = {
+ .ngpio = 4,
+ .ngpio_per_reg = 4,
+ .type = TN48M_GP0,
+};
+
+static const struct tn48m_gpio_config tn48m_gpi_config = {
+ .ngpio = 4,
+ .ngpio_per_reg = 4,
+ .type = TN48M_GPI,
+};
+
+static int tn48m_gpio_probe(struct platform_device *pdev)
+{
+ const struct tn48m_gpio_config *gpio_config;
+ struct gpio_regmap_config config = {};
+ struct regmap *regmap;
+ u32 base;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return -ENODEV;
+
+ gpio_config = device_get_match_data(&pdev->dev);
+ if (!gpio_config)
+ return -ENODEV;
+
+ ret = device_property_read_u32(&pdev->dev, "reg", &base);
+ if (ret)
+ return ret;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ config.regmap = regmap;
+ config.parent = &pdev->dev;
+ config.ngpio = gpio_config->ngpio;
+ config.ngpio_per_reg = gpio_config->ngpio_per_reg;
+ switch (gpio_config->type) {
+ case TN48M_GP0:
+ config.reg_set_base = base;
+ break;
+ case TN48M_GPI:
+ config.reg_dat_base = base;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
+}
+
+static const struct of_device_id tn48m_gpio_of_match[] = {
+ { .compatible = "delta,tn48m-gpo", .data = &tn48m_gpo_config },
+ { .compatible = "delta,tn48m-gpi", .data = &tn48m_gpi_config },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tn48m_gpio_of_match);
+
+static struct platform_driver tn48m_gpio_driver = {
+ .driver = {
+ .name = "delta-tn48m-gpio",
+ .of_match_table = tn48m_gpio_of_match,
+ },
+ .probe = tn48m_gpio_probe,
+};
+module_platform_driver(tn48m_gpio_driver);
+
+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
+MODULE_DESCRIPTION("Delta TN48M CPLD GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/greybus/svc.c b/drivers/greybus/svc.c
index ce7740ef449b..56d2b44d6fef 100644
--- a/drivers/greybus/svc.c
+++ b/drivers/greybus/svc.c
@@ -861,16 +861,26 @@ static int gb_svc_hello(struct gb_operation *op)
ret = gb_svc_watchdog_create(svc);
if (ret) {
dev_err(&svc->dev, "failed to create watchdog: %d\n", ret);
- goto err_unregister_device;
+ goto err_deregister_svc;
}
+ /*
+ * FIXME: This is a temporary hack to reconfigure the link at HELLO
+ * (which abuses the deferred request processing mechanism).
+ */
+ ret = gb_svc_queue_deferred_request(op);
+ if (ret)
+ goto err_destroy_watchdog;
+
gb_svc_debugfs_init(svc);
- return gb_svc_queue_deferred_request(op);
+ return 0;
-err_unregister_device:
+err_destroy_watchdog:
gb_svc_watchdog_destroy(svc);
+err_deregister_svc:
device_del(&svc->dev);
+
return ret;
}
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index db8bc55e5f50..68a8a27ab3b7 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1555,6 +1555,8 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
+source "drivers/hwmon/peci/Kconfig"
+
source "drivers/hwmon/pmbus/Kconfig"
config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 7fd5e94d88f6..8a03289e2aa4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -211,6 +211,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_SENSORS_OCC) += occ/
+obj-$(CONFIG_SENSORS_PECI) += peci/
obj-$(CONFIG_PMBUS) += pmbus/
ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig
new file mode 100644
index 000000000000..9d32a57badfe
--- /dev/null
+++ b/drivers/hwmon/peci/Kconfig
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SENSORS_PECI_CPUTEMP
+ tristate "PECI CPU temperature monitoring client"
+ depends on PECI
+ select SENSORS_PECI
+ select PECI_CPU
+ help
+ If you say yes here you get support for the generic Intel PECI
+ cputemp driver which provides Digital Thermal Sensor (DTS) thermal
+ readings of the CPU package and CPU cores that are accessible via
+ the processor PECI interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-cputemp.
+
+config SENSORS_PECI_DIMMTEMP
+ tristate "PECI DIMM temperature monitoring client"
+ depends on PECI
+ select SENSORS_PECI
+ select PECI_CPU
+ help
+ If you say yes here you get support for the generic Intel PECI hwmon
+ driver which provides Temperature Sensor on DIMM readings that are
+ accessible via the processor PECI interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-dimmtemp.
+
+config SENSORS_PECI
+ tristate
diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile
new file mode 100644
index 000000000000..191cfa0227f3
--- /dev/null
+++ b/drivers/hwmon/peci/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+peci-cputemp-y := cputemp.o
+peci-dimmtemp-y := dimmtemp.o
+
+obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o
+obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o
diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h
new file mode 100644
index 000000000000..734506b0eca2
--- /dev/null
+++ b/drivers/hwmon/peci/common.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2021 Intel Corporation */
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#ifndef __PECI_HWMON_COMMON_H
+#define __PECI_HWMON_COMMON_H
+
+#define PECI_HWMON_UPDATE_INTERVAL HZ
+
+/**
+ * struct peci_sensor_state - PECI state information
+ * @valid: flag to indicate the sensor value is valid
+ * @last_updated: time of the last update in jiffies
+ * @lock: mutex to protect sensor access
+ */
+struct peci_sensor_state {
+ bool valid;
+ unsigned long last_updated;
+ struct mutex lock; /* protect sensor access */
+};
+
+/**
+ * struct peci_sensor_data - PECI sensor information
+ * @value: sensor value in milli units
+ * @state: sensor update state
+ */
+
+struct peci_sensor_data {
+ s32 value;
+ struct peci_sensor_state state;
+};
+
+/**
+ * peci_sensor_need_update() - check whether sensor update is needed or not
+ * @sensor: pointer to sensor data struct
+ *
+ * Return: true if update is needed, false if not.
+ */
+
+static inline bool peci_sensor_need_update(struct peci_sensor_state *state)
+{
+ return !state->valid ||
+ time_after(jiffies, state->last_updated + PECI_HWMON_UPDATE_INTERVAL);
+}
+
+/**
+ * peci_sensor_mark_updated() - mark the sensor is updated
+ * @sensor: pointer to sensor data struct
+ */
+static inline void peci_sensor_mark_updated(struct peci_sensor_state *state)
+{
+ state->valid = true;
+ state->last_updated = jiffies;
+}
+
+#endif /* __PECI_HWMON_COMMON_H */
diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c
new file mode 100644
index 000000000000..12156328f5cf
--- /dev/null
+++ b/drivers/hwmon/peci/cputemp.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2021 Intel Corporation
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/peci.h>
+#include <linux/peci-cpu.h>
+#include <linux/units.h>
+
+#include "common.h"
+
+#define CORE_NUMS_MAX 64
+
+#define BASE_CHANNEL_NUMS 5
+#define CPUTEMP_CHANNEL_NUMS (BASE_CHANNEL_NUMS + CORE_NUMS_MAX)
+
+#define TEMP_TARGET_FAN_TEMP_MASK GENMASK(15, 8)
+#define TEMP_TARGET_REF_TEMP_MASK GENMASK(23, 16)
+#define TEMP_TARGET_TJ_OFFSET_MASK GENMASK(29, 24)
+
+#define DTS_MARGIN_MASK GENMASK(15, 0)
+#define PCS_MODULE_TEMP_MASK GENMASK(15, 0)
+
+struct resolved_cores_reg {
+ u8 bus;
+ u8 dev;
+ u8 func;
+ u8 offset;
+};
+
+struct cpu_info {
+ struct resolved_cores_reg *reg;
+ u8 min_peci_revision;
+ s32 (*thermal_margin_to_millidegree)(u16 val);
+};
+
+struct peci_temp_target {
+ s32 tcontrol;
+ s32 tthrottle;
+ s32 tjmax;
+ struct peci_sensor_state state;
+};
+
+enum peci_temp_target_type {
+ tcontrol_type,
+ tthrottle_type,
+ tjmax_type,
+ crit_hyst_type,
+};
+
+struct peci_cputemp {
+ struct peci_device *peci_dev;
+ struct device *dev;
+ const char *name;
+ const struct cpu_info *gen_info;
+ struct {
+ struct peci_temp_target target;
+ struct peci_sensor_data die;
+ struct peci_sensor_data dts;
+ struct peci_sensor_data core[CORE_NUMS_MAX];
+ } temp;
+ const char **coretemp_label;
+ DECLARE_BITMAP(core_mask, CORE_NUMS_MAX);
+};
+
+enum cputemp_channels {
+ channel_die,
+ channel_dts,
+ channel_tcontrol,
+ channel_tthrottle,
+ channel_tjmax,
+ channel_core,
+};
+
+static const char * const cputemp_label[BASE_CHANNEL_NUMS] = {
+ "Die",
+ "DTS",
+ "Tcontrol",
+ "Tthrottle",
+ "Tjmax",
+};
+
+static int update_temp_target(struct peci_cputemp *priv)
+{
+ s32 tthrottle_offset, tcontrol_margin;
+ u32 pcs;
+ int ret;
+
+ if (!peci_sensor_need_update(&priv->temp.target.state))
+ return 0;
+
+ ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs);
+ if (ret)
+ return ret;
+
+ priv->temp.target.tjmax =
+ FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * MILLIDEGREE_PER_DEGREE;
+
+ tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs);
+ tcontrol_margin = sign_extend32(tcontrol_margin, 7) * MILLIDEGREE_PER_DEGREE;
+ priv->temp.target.tcontrol = priv->temp.target.tjmax - tcontrol_margin;
+
+ tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * MILLIDEGREE_PER_DEGREE;
+ priv->temp.target.tthrottle = priv->temp.target.tjmax - tthrottle_offset;
+
+ peci_sensor_mark_updated(&priv->temp.target.state);
+
+ return 0;
+}
+
+static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type type, long *val)
+{
+ int ret;
+
+ mutex_lock(&priv->temp.target.state.lock);
+
+ ret = update_temp_target(priv);
+ if (ret)
+ goto unlock;
+
+ switch (type) {
+ case tcontrol_type:
+ *val = priv->temp.target.tcontrol;
+ break;
+ case tthrottle_type:
+ *val = priv->temp.target.tthrottle;
+ break;
+ case tjmax_type:
+ *val = priv->temp.target.tjmax;
+ break;
+ case crit_hyst_type:
+ *val = priv->temp.target.tjmax - priv->temp.target.tcontrol;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+unlock:
+ mutex_unlock(&priv->temp.target.state.lock);
+
+ return ret;
+}
+
+/*
+ * Error codes:
+ * 0x8000: General sensor error
+ * 0x8001: Reserved
+ * 0x8002: Underflow on reading value
+ * 0x8003-0x81ff: Reserved
+ */
+static bool dts_valid(u16 val)
+{
+ return val < 0x8000 || val > 0x81ff;
+}
+
+/*
+ * Processors return a value of DTS reading in S10.6 fixed point format
+ * (16 bits: 10-bit signed magnitude, 6-bit fraction).
+ */
+static s32 dts_ten_dot_six_to_millidegree(u16 val)
+{
+ return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 64;
+}
+
+/*
+ * For older processors, thermal margin reading is returned in S8.8 fixed
+ * point format (16 bits: 8-bit signed magnitude, 8-bit fraction).
+ */
+static s32 dts_eight_dot_eight_to_millidegree(u16 val)
+{
+ return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 256;
+}
+
+static int get_die_temp(struct peci_cputemp *priv, long *val)
+{
+ int ret = 0;
+ long tjmax;
+ u16 temp;
+
+ mutex_lock(&priv->temp.die.state.lock);
+ if (!peci_sensor_need_update(&priv->temp.die.state))
+ goto skip_update;
+
+ ret = peci_temp_read(priv->peci_dev, &temp);
+ if (ret)
+ goto err_unlock;
+
+ if (!dts_valid(temp)) {
+ ret = -EIO;
+ goto err_unlock;
+ }
+
+ ret = get_temp_target(priv, tjmax_type, &tjmax);
+ if (ret)
+ goto err_unlock;
+
+ priv->temp.die.value = (s32)tjmax + dts_ten_dot_six_to_millidegree(temp);
+
+ peci_sensor_mark_updated(&priv->temp.die.state);
+
+skip_update:
+ *val = priv->temp.die.value;
+err_unlock:
+ mutex_unlock(&priv->temp.die.state.lock);
+ return ret;
+}
+
+static int get_dts(struct peci_cputemp *priv, long *val)
+{
+ int ret = 0;
+ u16 thermal_margin;
+ long tcontrol;
+ u32 pcs;
+
+ mutex_lock(&priv->temp.dts.state.lock);
+ if (!peci_sensor_need_update(&priv->temp.dts.state))
+ goto skip_update;
+
+ ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs);
+ if (ret)
+ goto err_unlock;
+
+ thermal_margin = FIELD_GET(DTS_MARGIN_MASK, pcs);
+ if (!dts_valid(thermal_margin)) {
+ ret = -EIO;
+ goto err_unlock;
+ }
+
+ ret = get_temp_target(priv, tcontrol_type, &tcontrol);
+ if (ret)
+ goto err_unlock;
+
+ /* Note that the tcontrol should be available before calling it */
+ priv->temp.dts.value =
+ (s32)tcontrol - priv->gen_info->thermal_margin_to_millidegree(thermal_margin);
+
+ peci_sensor_mark_updated(&priv->temp.dts.state);
+
+skip_update:
+ *val = priv->temp.dts.value;
+err_unlock:
+ mutex_unlock(&priv->temp.dts.state.lock);
+ return ret;
+}
+
+static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val)
+{
+ int ret = 0;
+ u16 core_dts_margin;
+ long tjmax;
+ u32 pcs;
+
+ mutex_lock(&priv->temp.core[core_index].state.lock);
+ if (!peci_sensor_need_update(&priv->temp.core[core_index].state))
+ goto skip_update;
+
+ ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs);
+ if (ret)
+ goto err_unlock;
+
+ core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs);
+ if (!dts_valid(core_dts_margin)) {
+ ret = -EIO;
+ goto err_unlock;
+ }
+
+ ret = get_temp_target(priv, tjmax_type, &tjmax);
+ if (ret)
+ goto err_unlock;
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.core[core_index].value =
+ (s32)tjmax + dts_ten_dot_six_to_millidegree(core_dts_margin);
+
+ peci_sensor_mark_updated(&priv->temp.core[core_index].state);
+
+skip_update:
+ *val = priv->temp.core[core_index].value;
+err_unlock:
+ mutex_unlock(&priv->temp.core[core_index].state.lock);
+ return ret;
+}
+
+static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+
+ if (attr != hwmon_temp_label)
+ return -EOPNOTSUPP;
+
+ *str = channel < channel_core ?
+ cputemp_label[channel] : priv->coretemp_label[channel - channel_core];
+
+ return 0;
+}
+
+static int cputemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ switch (channel) {
+ case channel_die:
+ return get_die_temp(priv, val);
+ case channel_dts:
+ return get_dts(priv, val);
+ case channel_tcontrol:
+ return get_temp_target(priv, tcontrol_type, val);
+ case channel_tthrottle:
+ return get_temp_target(priv, tthrottle_type, val);
+ case channel_tjmax:
+ return get_temp_target(priv, tjmax_type, val);
+ default:
+ return get_core_temp(priv, channel - channel_core, val);
+ }
+ break;
+ case hwmon_temp_max:
+ return get_temp_target(priv, tcontrol_type, val);
+ case hwmon_temp_crit:
+ return get_temp_target(priv, tjmax_type, val);
+ case hwmon_temp_crit_hyst:
+ return get_temp_target(priv, crit_hyst_type, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct peci_cputemp *priv = data;
+
+ if (channel > CPUTEMP_CHANNEL_NUMS)
+ return 0;
+
+ if (channel < channel_core)
+ return 0444;
+
+ if (test_bit(channel - channel_core, priv->core_mask))
+ return 0444;
+
+ return 0;
+}
+
+static int init_core_mask(struct peci_cputemp *priv)
+{
+ struct peci_device *peci_dev = priv->peci_dev;
+ struct resolved_cores_reg *reg = priv->gen_info->reg;
+ u64 core_mask;
+ u32 data;
+ int ret;
+
+ /* Get the RESOLVED_CORES register value */
+ switch (peci_dev->info.model) {
+ case INTEL_FAM6_ICELAKE_X:
+ case INTEL_FAM6_ICELAKE_D:
+ ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev,
+ reg->func, reg->offset + 4, &data);
+ if (ret)
+ return ret;
+
+ core_mask = (u64)data << 32;
+
+ ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev,
+ reg->func, reg->offset, &data);
+ if (ret)
+ return ret;
+
+ core_mask |= data;
+
+ break;
+ default:
+ ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev,
+ reg->func, reg->offset, &data);
+ if (ret)
+ return ret;
+
+ core_mask = data;
+
+ break;
+ }
+
+ if (!core_mask)
+ return -EIO;
+
+ bitmap_from_u64(priv->core_mask, core_mask);
+
+ return 0;
+}
+
+static int create_temp_label(struct peci_cputemp *priv)
+{
+ unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX);
+ int i;
+
+ priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL);
+ if (!priv->coretemp_label)
+ return -ENOMEM;
+
+ for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) {
+ priv->coretemp_label[i] = devm_kasprintf(priv->dev, GFP_KERNEL, "Core %d", i);
+ if (!priv->coretemp_label[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void check_resolved_cores(struct peci_cputemp *priv)
+{
+ /*
+ * Failure to resolve cores is non-critical, we're still able to
+ * provide other sensor data.
+ */
+
+ if (init_core_mask(priv))
+ return;
+
+ if (create_temp_label(priv))
+ bitmap_zero(priv->core_mask, CORE_NUMS_MAX);
+}
+
+static void sensor_init(struct peci_cputemp *priv)
+{
+ int i;
+
+ mutex_init(&priv->temp.target.state.lock);
+ mutex_init(&priv->temp.die.state.lock);
+ mutex_init(&priv->temp.dts.state.lock);
+
+ for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX)
+ mutex_init(&priv->temp.core[i].state.lock);
+}
+
+static const struct hwmon_ops peci_cputemp_ops = {
+ .is_visible = cputemp_is_visible,
+ .read_string = cputemp_read_string,
+ .read = cputemp_read,
+};
+
+static const u32 peci_cputemp_temp_channel_config[] = {
+ /* Die temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST,
+ /* DTS margin */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST,
+ /* Tcontrol temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+ /* Tthrottle temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ /* Tjmax temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ /* Core temperature - for all core channels */
+ [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | HWMON_T_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info peci_cputemp_temp_channel = {
+ .type = hwmon_temp,
+ .config = peci_cputemp_temp_channel_config,
+};
+
+static const struct hwmon_channel_info *peci_cputemp_info[] = {
+ &peci_cputemp_temp_channel,
+ NULL
+};
+
+static const struct hwmon_chip_info peci_cputemp_chip_info = {
+ .ops = &peci_cputemp_ops,
+ .info = peci_cputemp_info,
+};
+
+static int peci_cputemp_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct peci_device *peci_dev = to_peci_device(dev->parent);
+ struct peci_cputemp *priv;
+ struct device *hwmon_dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d",
+ peci_dev->info.socket_id);
+ if (!priv->name)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->peci_dev = peci_dev;
+ priv->gen_info = (const struct cpu_info *)id->driver_data;
+
+ /*
+ * This is just a sanity check. Since we're using commands that are
+ * guaranteed to be supported on a given platform, we should never see
+ * revision lower than expected.
+ */
+ if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision)
+ dev_warn(priv->dev,
+ "Unexpected PECI revision %#x, some features may be unavailable\n",
+ peci_dev->info.peci_revision);
+
+ check_resolved_cores(priv);
+
+ sensor_init(priv);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name,
+ priv, &peci_cputemp_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * RESOLVED_CORES PCI configuration register may have different location on
+ * different platforms.
+ */
+static struct resolved_cores_reg resolved_cores_reg_hsx = {
+ .bus = 1,
+ .dev = 30,
+ .func = 3,
+ .offset = 0xb4,
+};
+
+static struct resolved_cores_reg resolved_cores_reg_icx = {
+ .bus = 14,
+ .dev = 30,
+ .func = 3,
+ .offset = 0xd0,
+};
+
+static const struct cpu_info cpu_hsx = {
+ .reg = &resolved_cores_reg_hsx,
+ .min_peci_revision = 0x33,
+ .thermal_margin_to_millidegree = &dts_eight_dot_eight_to_millidegree,
+};
+
+static const struct cpu_info cpu_icx = {
+ .reg = &resolved_cores_reg_icx,
+ .min_peci_revision = 0x40,
+ .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
+};
+
+static const struct auxiliary_device_id peci_cputemp_ids[] = {
+ {
+ .name = "peci_cpu.cputemp.hsx",
+ .driver_data = (kernel_ulong_t)&cpu_hsx,
+ },
+ {
+ .name = "peci_cpu.cputemp.bdx",
+ .driver_data = (kernel_ulong_t)&cpu_hsx,
+ },
+ {
+ .name = "peci_cpu.cputemp.bdxd",
+ .driver_data = (kernel_ulong_t)&cpu_hsx,
+ },
+ {
+ .name = "peci_cpu.cputemp.skx",
+ .driver_data = (kernel_ulong_t)&cpu_hsx,
+ },
+ {
+ .name = "peci_cpu.cputemp.icx",
+ .driver_data = (kernel_ulong_t)&cpu_icx,
+ },
+ {
+ .name = "peci_cpu.cputemp.icxd",
+ .driver_data = (kernel_ulong_t)&cpu_icx,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids);
+
+static struct auxiliary_driver peci_cputemp_driver = {
+ .probe = peci_cputemp_probe,
+ .id_table = peci_cputemp_ids,
+};
+
+module_auxiliary_driver(peci_cputemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
+MODULE_DESCRIPTION("PECI cputemp driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PECI_CPU);
diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c
new file mode 100644
index 000000000000..c8222354c005
--- /dev/null
+++ b/drivers/hwmon/peci/dimmtemp.c
@@ -0,0 +1,630 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2021 Intel Corporation
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/peci.h>
+#include <linux/peci-cpu.h>
+#include <linux/units.h>
+#include <linux/workqueue.h>
+
+#include "common.h"
+
+#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000)
+
+/* Max number of channel ranks and DIMM index per channel */
+#define CHAN_RANK_MAX_ON_HSX 8
+#define DIMM_IDX_MAX_ON_HSX 3
+#define CHAN_RANK_MAX_ON_BDX 4
+#define DIMM_IDX_MAX_ON_BDX 3
+#define CHAN_RANK_MAX_ON_BDXD 2
+#define DIMM_IDX_MAX_ON_BDXD 2
+#define CHAN_RANK_MAX_ON_SKX 6
+#define DIMM_IDX_MAX_ON_SKX 2
+#define CHAN_RANK_MAX_ON_ICX 8
+#define DIMM_IDX_MAX_ON_ICX 2
+#define CHAN_RANK_MAX_ON_ICXD 4
+#define DIMM_IDX_MAX_ON_ICXD 2
+
+#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
+#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
+#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX)
+
+#define CPU_SEG_MASK GENMASK(23, 16)
+#define GET_CPU_SEG(x) (((x) & CPU_SEG_MASK) >> 16)
+#define CPU_BUS_MASK GENMASK(7, 0)
+#define GET_CPU_BUS(x) ((x) & CPU_BUS_MASK)
+
+#define DIMM_TEMP_MAX GENMASK(15, 8)
+#define DIMM_TEMP_CRIT GENMASK(23, 16)
+#define GET_TEMP_MAX(x) (((x) & DIMM_TEMP_MAX) >> 8)
+#define GET_TEMP_CRIT(x) (((x) & DIMM_TEMP_CRIT) >> 16)
+
+#define NO_DIMM_RETRY_COUNT_MAX 5
+
+struct peci_dimmtemp;
+
+struct dimm_info {
+ int chan_rank_max;
+ int dimm_idx_max;
+ u8 min_peci_revision;
+ int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order,
+ int chan_rank, u32 *data);
+};
+
+struct peci_dimm_thresholds {
+ long temp_max;
+ long temp_crit;
+ struct peci_sensor_state state;
+};
+
+enum peci_dimm_threshold_type {
+ temp_max_type,
+ temp_crit_type,
+};
+
+struct peci_dimmtemp {
+ struct peci_device *peci_dev;
+ struct device *dev;
+ const char *name;
+ const struct dimm_info *gen_info;
+ struct delayed_work detect_work;
+ struct {
+ struct peci_sensor_data temp;
+ struct peci_dimm_thresholds thresholds;
+ } dimm[DIMM_NUMS_MAX];
+ char **dimmtemp_label;
+ DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX);
+ u8 no_dimm_retry_count;
+};
+
+static u8 __dimm_temp(u32 reg, int dimm_order)
+{
+ return (reg >> (dimm_order * 8)) & 0xff;
+}
+
+static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val)
+{
+ int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+ int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+ int ret = 0;
+ u32 data;
+
+ mutex_lock(&priv->dimm[dimm_no].temp.state.lock);
+ if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state))
+ goto skip_update;
+
+ ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data);
+ if (ret)
+ goto unlock;
+
+ priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE;
+
+ peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state);
+
+skip_update:
+ *val = priv->dimm[dimm_no].temp.value;
+unlock:
+ mutex_unlock(&priv->dimm[dimm_no].temp.state.lock);
+ return ret;
+}
+
+static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no)
+{
+ int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+ int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+ u32 data;
+ int ret;
+
+ if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state))
+ return 0;
+
+ ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data);
+ if (ret == -ENODATA) /* Use default or previous value */
+ return 0;
+ if (ret)
+ return ret;
+
+ priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * MILLIDEGREE_PER_DEGREE;
+ priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * MILLIDEGREE_PER_DEGREE;
+
+ peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state);
+
+ return 0;
+}
+
+static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_threshold_type type,
+ int dimm_no, long *val)
+{
+ int ret;
+
+ mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock);
+ ret = update_thresholds(priv, dimm_no);
+ if (ret)
+ goto unlock;
+
+ switch (type) {
+ case temp_max_type:
+ *val = priv->dimm[dimm_no].thresholds.temp_max;
+ break;
+ case temp_crit_type:
+ *val = priv->dimm[dimm_no].thresholds.temp_crit;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+unlock:
+ mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock);
+
+ return ret;
+}
+
+static int dimmtemp_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+
+ if (attr != hwmon_temp_label)
+ return -EOPNOTSUPP;
+
+ *str = (const char *)priv->dimmtemp_label[channel];
+
+ return 0;
+}
+
+static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return get_dimm_temp(priv, channel, val);
+ case hwmon_temp_max:
+ return get_dimm_thresholds(priv, temp_max_type, channel, val);
+ case hwmon_temp_crit:
+ return get_dimm_thresholds(priv, temp_crit_type, channel, val);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static umode_t dimmtemp_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct peci_dimmtemp *priv = data;
+
+ if (test_bit(channel, priv->dimm_mask))
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops peci_dimmtemp_ops = {
+ .is_visible = dimmtemp_is_visible,
+ .read_string = dimmtemp_read_string,
+ .read = dimmtemp_read,
+};
+
+static int check_populated_dimms(struct peci_dimmtemp *priv)
+{
+ int chan_rank_max = priv->gen_info->chan_rank_max;
+ int dimm_idx_max = priv->gen_info->dimm_idx_max;
+ u32 chan_rank_empty = 0;
+ u64 dimm_mask = 0;
+ int chan_rank, dimm_idx, ret;
+ u32 pcs;
+
+ BUILD_BUG_ON(BITS_PER_TYPE(chan_rank_empty) < CHAN_RANK_MAX);
+ BUILD_BUG_ON(BITS_PER_TYPE(dimm_mask) < DIMM_NUMS_MAX);
+ if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) {
+ WARN_ONCE(1, "Unsupported number of DIMMs - chan_rank_max: %d, dimm_idx_max: %d",
+ chan_rank_max, dimm_idx_max);
+ return -EINVAL;
+ }
+
+ for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+ ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &pcs);
+ if (ret) {
+ /*
+ * Overall, we expect either success or -EINVAL in
+ * order to determine whether DIMM is populated or not.
+ * For anything else we fall back to deferring the
+ * detection to be performed at a later point in time.
+ */
+ if (ret == -EINVAL) {
+ chan_rank_empty |= BIT(chan_rank);
+ continue;
+ }
+
+ return -EAGAIN;
+ }
+
+ for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+ if (__dimm_temp(pcs, dimm_idx))
+ dimm_mask |= BIT(chan_rank * dimm_idx_max + dimm_idx);
+ }
+
+ /*
+ * If we got all -EINVALs, it means that the CPU doesn't have any
+ * DIMMs. Unfortunately, it may also happen at the very start of
+ * host platform boot. Retrying a couple of times lets us make sure
+ * that the state is persistent.
+ */
+ if (chan_rank_empty == GENMASK(chan_rank_max - 1, 0)) {
+ if (priv->no_dimm_retry_count < NO_DIMM_RETRY_COUNT_MAX) {
+ priv->no_dimm_retry_count++;
+
+ return -EAGAIN;
+ }
+
+ return -ENODEV;
+ }
+
+ /*
+ * It's possible that memory training is not done yet. In this case we
+ * defer the detection to be performed at a later point in time.
+ */
+ if (!dimm_mask) {
+ priv->no_dimm_retry_count = 0;
+ return -EAGAIN;
+ }
+
+ dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask);
+
+ bitmap_from_u64(priv->dimm_mask, dimm_mask);
+
+ return 0;
+}
+
+static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan)
+{
+ int rank = chan / priv->gen_info->dimm_idx_max;
+ int idx = chan % priv->gen_info->dimm_idx_max;
+
+ priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL,
+ "DIMM %c%d", 'A' + rank,
+ idx + 1);
+ if (!priv->dimmtemp_label[chan])
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const u32 peci_dimmtemp_temp_channel_config[] = {
+ [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT,
+ 0
+};
+
+static const struct hwmon_channel_info peci_dimmtemp_temp_channel = {
+ .type = hwmon_temp,
+ .config = peci_dimmtemp_temp_channel_config,
+};
+
+static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = {
+ &peci_dimmtemp_temp_channel,
+ NULL
+};
+
+static const struct hwmon_chip_info peci_dimmtemp_chip_info = {
+ .ops = &peci_dimmtemp_ops,
+ .info = peci_dimmtemp_temp_info,
+};
+
+static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+{
+ int ret, i, channels;
+ struct device *dev;
+
+ /*
+ * We expect to either find populated DIMMs and carry on with creating
+ * sensors, or find out that there are no DIMMs populated.
+ * All other states mean that the platform never reached the state that
+ * allows to check DIMM state - causing us to retry later on.
+ */
+ ret = check_populated_dimms(priv);
+ if (ret == -ENODEV) {
+ dev_dbg(priv->dev, "No DIMMs found\n");
+ return 0;
+ } else if (ret) {
+ schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES);
+ dev_dbg(priv->dev, "Deferred populating DIMM temp info\n");
+ return ret;
+ }
+
+ channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max;
+
+ priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * sizeof(char *), GFP_KERNEL);
+ if (!priv->dimmtemp_label)
+ return -ENOMEM;
+
+ for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) {
+ ret = create_dimm_temp_label(priv, i);
+ if (ret)
+ return ret;
+ mutex_init(&priv->dimm[i].thresholds.state.lock);
+ mutex_init(&priv->dimm[i].temp.state.lock);
+ }
+
+ dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv,
+ &peci_dimmtemp_chip_info, NULL);
+ if (IS_ERR(dev)) {
+ dev_err(priv->dev, "Failed to register hwmon device\n");
+ return PTR_ERR(dev);
+ }
+
+ dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name);
+
+ return 0;
+}
+
+static void create_dimm_temp_info_delayed(struct work_struct *work)
+{
+ struct peci_dimmtemp *priv = container_of(to_delayed_work(work),
+ struct peci_dimmtemp,
+ detect_work);
+ int ret;
+
+ ret = create_dimm_temp_info(priv);
+ if (ret && ret != -EAGAIN)
+ dev_err(priv->dev, "Failed to populate DIMM temp info\n");
+}
+
+static void remove_delayed_work(void *_priv)
+{
+ struct peci_dimmtemp *priv = _priv;
+
+ cancel_delayed_work_sync(&priv->detect_work);
+}
+
+static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct peci_device *peci_dev = to_peci_device(dev->parent);
+ struct peci_dimmtemp *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d",
+ peci_dev->info.socket_id);
+ if (!priv->name)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->peci_dev = peci_dev;
+ priv->gen_info = (const struct dimm_info *)id->driver_data;
+
+ /*
+ * This is just a sanity check. Since we're using commands that are
+ * guaranteed to be supported on a given platform, we should never see
+ * revision lower than expected.
+ */
+ if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision)
+ dev_warn(priv->dev,
+ "Unexpected PECI revision %#x, some features may be unavailable\n",
+ peci_dev->info.peci_revision);
+
+ INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed);
+
+ ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, priv);
+ if (ret)
+ return ret;
+
+ ret = create_dimm_temp_info(priv);
+ if (ret && ret != -EAGAIN) {
+ dev_err(dev, "Failed to populate DIMM temp info\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
+{
+ u8 dev, func;
+ u16 reg;
+ int ret;
+
+ /*
+ * Device 20, Function 0: IMC 0 channel 0 -> rank 0
+ * Device 20, Function 1: IMC 0 channel 1 -> rank 1
+ * Device 21, Function 0: IMC 0 channel 2 -> rank 2
+ * Device 21, Function 1: IMC 0 channel 3 -> rank 3
+ * Device 23, Function 0: IMC 1 channel 0 -> rank 4
+ * Device 23, Function 1: IMC 1 channel 1 -> rank 5
+ * Device 24, Function 0: IMC 1 channel 2 -> rank 6
+ * Device 24, Function 1: IMC 1 channel 3 -> rank 7
+ */
+ dev = 20 + chan_rank / 2 + chan_rank / 4;
+ func = chan_rank % 2;
+ reg = 0x120 + dimm_order * 4;
+
+ ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+read_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
+{
+ u8 dev, func;
+ u16 reg;
+ int ret;
+
+ /*
+ * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+ * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+ * Device 12, Function 2: IMC 1 channel 0 -> rank 2
+ * Device 12, Function 6: IMC 1 channel 1 -> rank 3
+ */
+ dev = 10 + chan_rank / 2 * 2;
+ func = (chan_rank % 2) ? 6 : 2;
+ reg = 0x120 + dimm_order * 4;
+
+ ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+read_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
+{
+ u8 dev, func;
+ u16 reg;
+ int ret;
+
+ /*
+ * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+ * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+ * Device 11, Function 2: IMC 0 channel 2 -> rank 2
+ * Device 12, Function 2: IMC 1 channel 0 -> rank 3
+ * Device 12, Function 6: IMC 1 channel 1 -> rank 4
+ * Device 13, Function 2: IMC 1 channel 2 -> rank 5
+ */
+ dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0);
+ func = chan_rank % 3 == 1 ? 6 : 2;
+ reg = 0x120 + dimm_order * 4;
+
+ ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
+{
+ u32 reg_val;
+ u64 offset;
+ int ret;
+ u8 dev;
+
+ ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, &reg_val);
+ if (ret || !(reg_val & BIT(31)))
+ return -ENODATA; /* Use default or previous value */
+
+ ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, &reg_val);
+ if (ret)
+ return -ENODATA; /* Use default or previous value */
+
+ /*
+ * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0
+ * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1
+ * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2
+ * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3
+ * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4
+ * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5
+ * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6
+ * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7
+ */
+ dev = 26 + chan_rank / 2;
+ offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000;
+
+ ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val),
+ dev, 0, offset, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct dimm_info dimm_hsx = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_HSX,
+ .min_peci_revision = 0x33,
+ .read_thresholds = &read_thresholds_hsx,
+};
+
+static const struct dimm_info dimm_bdx = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_BDX,
+ .min_peci_revision = 0x33,
+ .read_thresholds = &read_thresholds_hsx,
+};
+
+static const struct dimm_info dimm_bdxd = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_BDXD,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_BDXD,
+ .min_peci_revision = 0x33,
+ .read_thresholds = &read_thresholds_bdxd,
+};
+
+static const struct dimm_info dimm_skx = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_SKX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_SKX,
+ .min_peci_revision = 0x33,
+ .read_thresholds = &read_thresholds_skx,
+};
+
+static const struct dimm_info dimm_icx = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_ICX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_ICX,
+ .min_peci_revision = 0x40,
+ .read_thresholds = &read_thresholds_icx,
+};
+
+static const struct dimm_info dimm_icxd = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_ICXD,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_ICXD,
+ .min_peci_revision = 0x40,
+ .read_thresholds = &read_thresholds_icx,
+};
+
+static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
+ {
+ .name = "peci_cpu.dimmtemp.hsx",
+ .driver_data = (kernel_ulong_t)&dimm_hsx,
+ },
+ {
+ .name = "peci_cpu.dimmtemp.bdx",
+ .driver_data = (kernel_ulong_t)&dimm_bdx,
+ },
+ {
+ .name = "peci_cpu.dimmtemp.bdxd",
+ .driver_data = (kernel_ulong_t)&dimm_bdxd,
+ },
+ {
+ .name = "peci_cpu.dimmtemp.skx",
+ .driver_data = (kernel_ulong_t)&dimm_skx,
+ },
+ {
+ .name = "peci_cpu.dimmtemp.icx",
+ .driver_data = (kernel_ulong_t)&dimm_icx,
+ },
+ {
+ .name = "peci_cpu.dimmtemp.icxd",
+ .driver_data = (kernel_ulong_t)&dimm_icxd,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);
+
+static struct auxiliary_driver peci_dimmtemp_driver = {
+ .probe = peci_dimmtemp_probe,
+ .id_table = peci_dimmtemp_ids,
+};
+
+module_auxiliary_driver(peci_dimmtemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
+MODULE_DESCRIPTION("PECI dimmtemp driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PECI_CPU);
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 88653d1c06a4..af00dca8d1ac 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1279,9 +1279,6 @@ ATTRIBUTE_GROUPS(coresight_source);
static struct device_type coresight_dev_type[] = {
{
- .name = "none",
- },
- {
.name = "sink",
.groups = coresight_sink_groups,
},
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c
index cf64ce73a741..7d413ba8b823 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c
@@ -340,6 +340,10 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata,
config->ctrl = attr->config;
+ /* Don't trace contextID when runs in non-root PID namespace */
+ if (!task_is_in_init_pid_ns(current))
+ config->ctrl &= ~ETMCR_CTXID_SIZE;
+
/*
* Possible to have cores with PTM (supports ret stack) and ETM
* (never has ret stack) on the same SoC. So if we have a request
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index bf18128cf5de..7f416a12000e 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -656,7 +656,9 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
config->cfg |= BIT(11);
}
- if (attr->config & BIT(ETM_OPT_CTXTID))
+ /* Only trace contextID when runs in root PID namespace */
+ if ((attr->config & BIT(ETM_OPT_CTXTID)) &&
+ task_is_in_init_pid_ns(current))
/* bit[6], Context ID tracing bit */
config->cfg |= BIT(ETM4_CFG_BIT_CTXTID);
@@ -670,7 +672,11 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
ret = -EINVAL;
goto out;
}
- config->cfg |= BIT(ETM4_CFG_BIT_VMID) | BIT(ETM4_CFG_BIT_VMID_OPT);
+
+ /* Only trace virtual contextID when runs in root PID namespace */
+ if (task_is_in_init_pid_ns(current))
+ config->cfg |= BIT(ETM4_CFG_BIT_VMID) |
+ BIT(ETM4_CFG_BIT_VMID_OPT);
}
/* return stack - enable if selected and supported */
@@ -1091,7 +1097,7 @@ static void etm4_init_arch_data(void *info)
etmidr0 = etm4x_relaxed_read32(csa, TRCIDR0);
/* INSTP0, bits[2:1] P0 tracing support field */
- if (BMVAL(etmidr0, 1, 1) && BMVAL(etmidr0, 2, 2))
+ if (BMVAL(etmidr0, 1, 2) == 0b11)
drvdata->instrp0 = true;
else
drvdata->instrp0 = false;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index a0640fa5c55b..21687cc1e4e2 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -367,8 +367,12 @@ static ssize_t mode_store(struct device *dev,
mode = ETM_MODE_QELEM(config->mode);
/* start by clearing QE bits */
config->cfg &= ~(BIT(13) | BIT(14));
- /* if supported, Q elements with instruction counts are enabled */
- if ((mode & BIT(0)) && (drvdata->q_support & BIT(0)))
+ /*
+ * if supported, Q elements with instruction counts are enabled.
+ * Always set the low bit for any requested mode. Valid combos are
+ * 0b00, 0b01 and 0b11.
+ */
+ if (mode && drvdata->q_support)
config->cfg |= BIT(13);
/*
* if supported, Q elements with and without instruction
@@ -2111,7 +2115,16 @@ static ssize_t vmid_val_show(struct device *dev,
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
+ /*
+ * Don't use virtual contextID tracing if coming from a PID namespace.
+ * See comment in ctxid_pid_store().
+ */
+ if (!task_is_in_init_pid_ns(current))
+ return -EINVAL;
+
+ spin_lock(&drvdata->spinlock);
val = (unsigned long)config->vmid_val[config->vmid_idx];
+ spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
@@ -2124,6 +2137,13 @@ static ssize_t vmid_val_store(struct device *dev,
struct etmv4_config *config = &drvdata->config;
/*
+ * Don't use virtual contextID tracing if coming from a PID namespace.
+ * See comment in ctxid_pid_store().
+ */
+ if (!task_is_in_init_pid_ns(current))
+ return -EINVAL;
+
+ /*
* only implemented when vmid tracing is enabled, i.e. at least one
* vmid comparator is implemented and at least 8 bit vmid size
*/
@@ -2146,6 +2166,13 @@ static ssize_t vmid_masks_show(struct device *dev,
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etmv4_config *config = &drvdata->config;
+ /*
+ * Don't use virtual contextID tracing if coming from a PID namespace.
+ * See comment in ctxid_pid_store().
+ */
+ if (!task_is_in_init_pid_ns(current))
+ return -EINVAL;
+
spin_lock(&drvdata->spinlock);
val1 = config->vmid_mask0;
val2 = config->vmid_mask1;
@@ -2164,6 +2191,13 @@ static ssize_t vmid_masks_store(struct device *dev,
int nr_inputs;
/*
+ * Don't use virtual contextID tracing if coming from a PID namespace.
+ * See comment in ctxid_pid_store().
+ */
+ if (!task_is_in_init_pid_ns(current))
+ return -EINVAL;
+
+ /*
* only implemented when vmid tracing is enabled, i.e. at least one
* vmid comparator is implemented and at least 8 bit vmid size
*/
diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
index c594f45319fc..475899714104 100644
--- a/drivers/hwtracing/coresight/coresight-platform.c
+++ b/drivers/hwtracing/coresight/coresight-platform.c
@@ -626,7 +626,7 @@ static int acpi_coresight_parse_link(struct acpi_device *adev,
const union acpi_object *link,
struct coresight_connection *conn)
{
- int rc, dir;
+ int dir;
const union acpi_object *fields;
struct acpi_device *r_adev;
struct device *rdev;
@@ -643,9 +643,9 @@ static int acpi_coresight_parse_link(struct acpi_device *adev,
fields[3].type != ACPI_TYPE_INTEGER)
return -EINVAL;
- rc = acpi_bus_get_device(fields[2].reference.handle, &r_adev);
- if (rc)
- return rc;
+ r_adev = acpi_fetch_acpi_dev(fields[2].reference.handle);
+ if (!r_adev)
+ return -ENODEV;
dir = fields[3].integer.value;
if (dir == ACPI_CORESIGHT_LINK_MASTER) {
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
index 098fc34c4829..11850fd8c3b5 100644
--- a/drivers/hwtracing/coresight/coresight-syscfg.c
+++ b/drivers/hwtracing/coresight/coresight-syscfg.c
@@ -1049,7 +1049,7 @@ static int cscfg_create_device(void)
err = device_register(dev);
if (err)
- cscfg_dev_release(dev);
+ put_device(dev);
create_dev_exit_unlock:
mutex_unlock(&cscfg_mutex);
diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
index 276862c07e32..2b386bb848f8 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.c
+++ b/drivers/hwtracing/coresight/coresight-trbe.c
@@ -91,10 +91,16 @@ struct trbe_buf {
*/
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0
#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE 1
+#define TRBE_NEEDS_DRAIN_AFTER_DISABLE 2
+#define TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE 3
+#define TRBE_IS_BROKEN 4
static int trbe_errata_cpucaps[] = {
[TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE,
[TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE,
+ [TRBE_NEEDS_DRAIN_AFTER_DISABLE] = ARM64_WORKAROUND_2064142,
+ [TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE] = ARM64_WORKAROUND_2038923,
+ [TRBE_IS_BROKEN] = ARM64_WORKAROUND_1902691,
-1, /* Sentinel, must be the last entry */
};
@@ -167,6 +173,32 @@ static inline bool trbe_may_write_out_of_range(struct trbe_cpudata *cpudata)
return trbe_has_erratum(cpudata, TRBE_WORKAROUND_WRITE_OUT_OF_RANGE);
}
+static inline bool trbe_needs_drain_after_disable(struct trbe_cpudata *cpudata)
+{
+ /*
+ * Errata affected TRBE implementation will need TSB CSYNC and
+ * DSB in order to prevent subsequent writes into certain TRBE
+ * system registers from being ignored and not effected.
+ */
+ return trbe_has_erratum(cpudata, TRBE_NEEDS_DRAIN_AFTER_DISABLE);
+}
+
+static inline bool trbe_needs_ctxt_sync_after_enable(struct trbe_cpudata *cpudata)
+{
+ /*
+ * Errata affected TRBE implementation will need an additional
+ * context synchronization in order to prevent an inconsistent
+ * TRBE prohibited region view on the CPU which could possibly
+ * corrupt the TRBE buffer or the TRBE state.
+ */
+ return trbe_has_erratum(cpudata, TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE);
+}
+
+static inline bool trbe_is_broken(struct trbe_cpudata *cpudata)
+{
+ return trbe_has_erratum(cpudata, TRBE_IS_BROKEN);
+}
+
static int trbe_alloc_node(struct perf_event *event)
{
if (event->cpu == -1)
@@ -174,17 +206,31 @@ static int trbe_alloc_node(struct perf_event *event)
return cpu_to_node(event->cpu);
}
-static void trbe_drain_buffer(void)
+static inline void trbe_drain_buffer(void)
{
tsb_csync();
dsb(nsh);
}
-static void trbe_drain_and_disable_local(void)
+static inline void set_trbe_enabled(struct trbe_cpudata *cpudata, u64 trblimitr)
{
- u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
+ /*
+ * Enable the TRBE without clearing LIMITPTR which
+ * might be required for fetching the buffer limits.
+ */
+ trblimitr |= TRBLIMITR_ENABLE;
+ write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1);
- trbe_drain_buffer();
+ /* Synchronize the TRBE enable event */
+ isb();
+
+ if (trbe_needs_ctxt_sync_after_enable(cpudata))
+ isb();
+}
+
+static inline void set_trbe_disabled(struct trbe_cpudata *cpudata)
+{
+ u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
/*
* Disable the TRBE without clearing LIMITPTR which
@@ -192,12 +238,21 @@ static void trbe_drain_and_disable_local(void)
*/
trblimitr &= ~TRBLIMITR_ENABLE;
write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1);
+
+ if (trbe_needs_drain_after_disable(cpudata))
+ trbe_drain_buffer();
isb();
}
-static void trbe_reset_local(void)
+static void trbe_drain_and_disable_local(struct trbe_cpudata *cpudata)
{
- trbe_drain_and_disable_local();
+ trbe_drain_buffer();
+ set_trbe_disabled(cpudata);
+}
+
+static void trbe_reset_local(struct trbe_cpudata *cpudata)
+{
+ trbe_drain_and_disable_local(cpudata);
write_sysreg_s(0, SYS_TRBLIMITR_EL1);
write_sysreg_s(0, SYS_TRBPTR_EL1);
write_sysreg_s(0, SYS_TRBBASER_EL1);
@@ -234,7 +289,7 @@ static void trbe_stop_and_truncate_event(struct perf_output_handle *handle)
* at event_stop(). So disable the TRBE here and leave
* the update_buffer() to return a 0 size.
*/
- trbe_drain_and_disable_local();
+ trbe_drain_and_disable_local(buf->cpudata);
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
perf_aux_output_end(handle, 0);
*this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL;
@@ -536,9 +591,10 @@ static void clr_trbe_status(void)
write_sysreg_s(trbsr, SYS_TRBSR_EL1);
}
-static void set_trbe_limit_pointer_enabled(unsigned long addr)
+static void set_trbe_limit_pointer_enabled(struct trbe_buf *buf)
{
u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
+ unsigned long addr = buf->trbe_limit;
WARN_ON(!IS_ALIGNED(addr, (1UL << TRBLIMITR_LIMIT_SHIFT)));
WARN_ON(!IS_ALIGNED(addr, PAGE_SIZE));
@@ -566,12 +622,7 @@ static void set_trbe_limit_pointer_enabled(unsigned long addr)
trblimitr |= (TRBE_TRIG_MODE_IGNORE & TRBLIMITR_TRIG_MODE_MASK) <<
TRBLIMITR_TRIG_MODE_SHIFT;
trblimitr |= (addr & PAGE_MASK);
-
- trblimitr |= TRBLIMITR_ENABLE;
- write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1);
-
- /* Synchronize the TRBE enable event */
- isb();
+ set_trbe_enabled(buf->cpudata, trblimitr);
}
static void trbe_enable_hw(struct trbe_buf *buf)
@@ -579,8 +630,7 @@ static void trbe_enable_hw(struct trbe_buf *buf)
WARN_ON(buf->trbe_hw_base < buf->trbe_base);
WARN_ON(buf->trbe_write < buf->trbe_hw_base);
WARN_ON(buf->trbe_write >= buf->trbe_limit);
- set_trbe_disabled();
- isb();
+ set_trbe_disabled(buf->cpudata);
clr_trbe_status();
set_trbe_base_pointer(buf->trbe_hw_base);
set_trbe_write_pointer(buf->trbe_write);
@@ -590,7 +640,7 @@ static void trbe_enable_hw(struct trbe_buf *buf)
* till now before enabling the TRBE.
*/
isb();
- set_trbe_limit_pointer_enabled(buf->trbe_limit);
+ set_trbe_limit_pointer_enabled(buf);
}
static enum trbe_fault_action trbe_get_fault_act(struct perf_output_handle *handle,
@@ -775,7 +825,7 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev,
* the TRBE here will ensure that no IRQ could be generated when the perf
* handle gets freed in etm_event_stop().
*/
- trbe_drain_and_disable_local();
+ trbe_drain_and_disable_local(cpudata);
/* Check if there is a pending interrupt and handle it here */
status = read_sysreg_s(SYS_TRBSR_EL1);
@@ -986,7 +1036,7 @@ static int arm_trbe_disable(struct coresight_device *csdev)
if (cpudata->mode != CS_MODE_PERF)
return -EINVAL;
- trbe_drain_and_disable_local();
+ trbe_drain_and_disable_local(cpudata);
buf->cpudata = NULL;
cpudata->buf = NULL;
cpudata->mode = CS_MODE_DISABLED;
@@ -995,16 +1045,15 @@ static int arm_trbe_disable(struct coresight_device *csdev)
static void trbe_handle_spurious(struct perf_output_handle *handle)
{
- u64 limitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
+ struct trbe_buf *buf = etm_perf_sink_config(handle);
+ u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
/*
* If the IRQ was spurious, simply re-enable the TRBE
* back without modifying the buffer parameters to
* retain the trace collected so far.
*/
- limitr |= TRBLIMITR_ENABLE;
- write_sysreg_s(limitr, SYS_TRBLIMITR_EL1);
- isb();
+ set_trbe_enabled(buf->cpudata, trblimitr);
}
static int trbe_handle_overflow(struct perf_output_handle *handle)
@@ -1028,7 +1077,7 @@ static int trbe_handle_overflow(struct perf_output_handle *handle)
* is able to detect this with a disconnected handle
* (handle->event = NULL).
*/
- trbe_drain_and_disable_local();
+ trbe_drain_and_disable_local(buf->cpudata);
*this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL;
return -EINVAL;
}
@@ -1062,6 +1111,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
{
struct perf_output_handle **handle_ptr = dev;
struct perf_output_handle *handle = *handle_ptr;
+ struct trbe_buf *buf = etm_perf_sink_config(handle);
enum trbe_fault_action act;
u64 status;
bool truncated = false;
@@ -1082,7 +1132,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
* Ensure the trace is visible to the CPUs and
* any external aborts have been resolved.
*/
- trbe_drain_and_disable_local();
+ trbe_drain_and_disable_local(buf->cpudata);
clr_trbe_irq();
isb();
@@ -1167,8 +1217,9 @@ static const struct attribute_group *arm_trbe_groups[] = {
static void arm_trbe_enable_cpu(void *info)
{
struct trbe_drvdata *drvdata = info;
+ struct trbe_cpudata *cpudata = this_cpu_ptr(drvdata->cpudata);
- trbe_reset_local();
+ trbe_reset_local(cpudata);
enable_percpu_irq(drvdata->irq, IRQ_TYPE_NONE);
}
@@ -1244,6 +1295,11 @@ static void arm_trbe_probe_cpu(void *info)
*/
trbe_check_errata(cpudata);
+ if (trbe_is_broken(cpudata)) {
+ pr_err("Disabling TRBE on cpu%d due to erratum\n", cpu);
+ goto cpu_clear;
+ }
+
/*
* If the TRBE is affected by erratum TRBE_WORKAROUND_OVERWRITE_FILL_MODE,
* we must always program the TBRPTR_EL1, 256bytes from a page
@@ -1276,7 +1332,7 @@ static void arm_trbe_remove_coresight_cpu(void *info)
struct coresight_device *trbe_csdev = coresight_get_percpu_sink(cpu);
disable_percpu_irq(drvdata->irq);
- trbe_reset_local();
+ trbe_reset_local(cpudata);
if (trbe_csdev) {
coresight_unregister(trbe_csdev);
cpudata->drvdata = NULL;
@@ -1349,8 +1405,10 @@ static int arm_trbe_cpu_teardown(unsigned int cpu, struct hlist_node *node)
struct trbe_drvdata *drvdata = hlist_entry_safe(node, struct trbe_drvdata, hotplug_node);
if (cpumask_test_cpu(cpu, &drvdata->supported_cpus)) {
+ struct trbe_cpudata *cpudata = per_cpu_ptr(drvdata->cpudata, cpu);
+
disable_percpu_irq(drvdata->irq);
- trbe_reset_local();
+ trbe_reset_local(cpudata);
}
return 0;
}
@@ -1423,6 +1481,12 @@ static int arm_trbe_device_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
int ret;
+ /* Trace capture is not possible with kernel page table isolation */
+ if (arm64_kernel_unmapped_at_el0()) {
+ pr_err("TRBE wouldn't work if kernel gets unmapped at EL0\n");
+ return -EOPNOTSUPP;
+ }
+
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
@@ -1484,11 +1548,6 @@ static int __init arm_trbe_init(void)
{
int ret;
- if (arm64_kernel_unmapped_at_el0()) {
- pr_err("TRBE wouldn't work if kernel gets unmapped at EL0\n");
- return -EOPNOTSUPP;
- }
-
ret = platform_driver_register(&arm_trbe_driver);
if (!ret)
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-trbe.h b/drivers/hwtracing/coresight/coresight-trbe.h
index abf3e36082f0..30e4d7db4f8e 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.h
+++ b/drivers/hwtracing/coresight/coresight-trbe.h
@@ -91,14 +91,6 @@ static inline bool is_trbe_running(u64 trbsr)
#define TRBE_FILL_MODE_WRAP 1
#define TRBE_FILL_MODE_CIRCULAR_BUFFER 3
-static inline void set_trbe_disabled(void)
-{
- u64 trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
-
- trblimitr &= ~TRBLIMITR_ENABLE;
- write_sysreg_s(trblimitr, SYS_TRBLIMITR_EL1);
-}
-
static inline bool get_trbe_flag_update(u64 trbidr)
{
return trbidr & TRBIDR_FLAG;
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 49587c992a6d..eac3f02662ae 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -123,6 +123,33 @@ config ADXL355_SPI
will be called adxl355_spi and you will also get adxl355_core
for the core module.
+config ADXL367
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config ADXL367_SPI
+ tristate "Analog Devices ADXL367 3-Axis Accelerometer SPI Driver"
+ depends on SPI
+ select ADXL367
+ select REGMAP_SPI
+ help
+ Say yes here to add support for the Analog Devices ADXL367 triaxial
+ acceleration sensor.
+ To compile this driver as a module, choose M here: the
+ module will be called adxl367_spi.
+
+config ADXL367_I2C
+ tristate "Analog Devices ADXL367 3-Axis Accelerometer I2C Driver"
+ depends on I2C
+ select ADXL367
+ select REGMAP_I2C
+ help
+ Say yes here to add support for the Analog Devices ADXL367 triaxial
+ acceleration sensor.
+ To compile this driver as a module, choose M here: the
+ module will be called adxl367_i2c.
+
config ADXL372
tristate
select IIO_BUFFER
@@ -349,8 +376,6 @@ config IIO_ST_ACCEL_3AXIS
depends on !SENSORS_LIS3_I2C
depends on !SENSORS_LIS3_SPI
select IIO_ST_SENSORS_CORE
- select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
- select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics accelerometers:
@@ -358,23 +383,30 @@ config IIO_ST_ACCEL_3AXIS
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
LNG2DM, LIS3DE, LIS2DE12, LIS2HH12
- This driver can also be built as a module. If so, these modules
- will be created:
- - st_accel (core functions for the driver [it is mandatory]);
- - st_accel_i2c (necessary for the I2C devices [optional*]);
- - st_accel_spi (necessary for the SPI devices [optional*]);
-
- (*) one of these is necessary to do something.
+ Also need to enable at least one of I2C and SPI interface drivers
+ below.
config IIO_ST_ACCEL_I2C_3AXIS
- tristate
- depends on IIO_ST_ACCEL_3AXIS
- depends on IIO_ST_SENSORS_I2C
+ tristate "STMicroelectronics accelerometers 3-Axis I2C Interface"
+ depends on I2C && IIO_ST_ACCEL_3AXIS
+ default I2C && IIO_ST_ACCEL_3AXIS
+ select IIO_ST_SENSORS_I2C
+ help
+ Build support for STMicroelectronics accelerometers I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_accel_i2c.
config IIO_ST_ACCEL_SPI_3AXIS
- tristate
- depends on IIO_ST_ACCEL_3AXIS
- depends on IIO_ST_SENSORS_SPI
+ tristate "STMicroelectronics accelerometers 3-Axis SPI Interface"
+ depends on SPI_MASTER && IIO_ST_ACCEL_3AXIS
+ default SPI_MASTER && IIO_ST_ACCEL_3AXIS
+ select IIO_ST_SENSORS_SPI
+ help
+ Build support for STMicroelectronics accelerometers SPI interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_accel_spi.
config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver"
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index d03e2f6bba08..4d8792668838 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -15,6 +15,9 @@ obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o
obj-$(CONFIG_ADXL355) += adxl355_core.o
obj-$(CONFIG_ADXL355_I2C) += adxl355_i2c.o
obj-$(CONFIG_ADXL355_SPI) += adxl355_spi.o
+obj-$(CONFIG_ADXL367) += adxl367.o
+obj-$(CONFIG_ADXL367_I2C) += adxl367_i2c.o
+obj-$(CONFIG_ADXL367_SPI) += adxl367_spi.o
obj-$(CONFIG_ADXL372) += adxl372.o
obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o
obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o
diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c
index 7a434e2884d4..dfb8e2e5bdf5 100644
--- a/drivers/iio/accel/adis16201.c
+++ b/drivers/iio/accel/adis16201.c
@@ -300,3 +300,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:adis16201");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c
index ac08e866d612..5a9c6e2296f1 100644
--- a/drivers/iio/accel/adis16209.c
+++ b/drivers/iio/accel/adis16209.c
@@ -310,3 +310,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:adis16209");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 0d243341f1a7..9e4193e64765 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -26,7 +26,7 @@ const struct regmap_access_table adxl313_readable_regs_table = {
.yes_ranges = adxl313_readable_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
};
-EXPORT_SYMBOL_GPL(adxl313_readable_regs_table);
+EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
static const struct regmap_range adxl313_writable_reg_range[] = {
regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
@@ -41,7 +41,7 @@ const struct regmap_access_table adxl313_writable_regs_table = {
.yes_ranges = adxl313_writable_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
};
-EXPORT_SYMBOL_GPL(adxl313_writable_regs_table);
+EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
struct adxl313_data {
struct regmap *regmap;
@@ -325,7 +325,7 @@ int adxl313_core_probe(struct device *dev,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(adxl313_core_probe);
+EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);
MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver");
diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c
index 82e9fb2db1e6..c329765dbf60 100644
--- a/drivers/iio/accel/adxl313_i2c.c
+++ b/drivers/iio/accel/adxl313_i2c.c
@@ -64,3 +64,4 @@ module_i2c_driver(adxl313_i2c_driver);
MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer I2C driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL313);
diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
index a6162f36ef52..a3c6d553462d 100644
--- a/drivers/iio/accel/adxl313_spi.c
+++ b/drivers/iio/accel/adxl313_spi.c
@@ -90,3 +90,4 @@ module_spi_driver(adxl313_spi_driver);
MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL313);
diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h
index af0fdd02c4f2..d7e67cb08538 100644
--- a/drivers/iio/accel/adxl345.h
+++ b/drivers/iio/accel/adxl345.h
@@ -9,11 +9,10 @@
#define _ADXL345_H_
enum adxl345_device_type {
- ADXL345,
- ADXL375,
+ ADXL345 = 1,
+ ADXL375 = 2,
};
-int adxl345_core_probe(struct device *dev, struct regmap *regmap,
- enum adxl345_device_type type, const char *name);
+int adxl345_core_probe(struct device *dev, struct regmap *regmap);
#endif /* _ADXL345_H_ */
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 4b275051ef61..370bfec1275a 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -8,6 +8,7 @@
*/
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
@@ -194,7 +195,7 @@ static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
static struct attribute *adxl345_attrs[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
- NULL,
+ NULL
};
static const struct attribute_group adxl345_attrs_group = {
@@ -208,30 +209,44 @@ static const struct iio_info adxl345_info = {
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
};
+static int adxl345_powerup(void *regmap)
+{
+ return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE);
+}
+
static void adxl345_powerdown(void *regmap)
{
regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY);
}
-int adxl345_core_probe(struct device *dev, struct regmap *regmap,
- enum adxl345_device_type type, const char *name)
+int adxl345_core_probe(struct device *dev, struct regmap *regmap)
{
+ enum adxl345_device_type type;
struct adxl345_data *data;
struct iio_dev *indio_dev;
+ const char *name;
u32 regval;
int ret;
- ret = regmap_read(regmap, ADXL345_REG_DEVID, &regval);
- if (ret < 0) {
- dev_err(dev, "Error reading device ID: %d\n", ret);
- return ret;
+ type = (uintptr_t)device_get_match_data(dev);
+ switch (type) {
+ case ADXL345:
+ name = "adxl345";
+ break;
+ case ADXL375:
+ name = "adxl375";
+ break;
+ default:
+ return -EINVAL;
}
- if (regval != ADXL345_DEVID) {
- dev_err(dev, "Invalid device ID: %x, expected %x\n",
- regval, ADXL345_DEVID);
- return -ENODEV;
- }
+ ret = regmap_read(regmap, ADXL345_REG_DEVID, &regval);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error reading device ID\n");
+
+ if (regval != ADXL345_DEVID)
+ return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
+ regval, ADXL345_DEVID);
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -245,10 +260,8 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
data->data_range);
- if (ret < 0) {
- dev_err(dev, "Failed to set data range: %d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to set data range\n");
indio_dev->name = name;
indio_dev->info = &adxl345_info;
@@ -257,12 +270,9 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
/* Enable measurement mode */
- ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
- ADXL345_POWER_CTL_MEASURE);
- if (ret < 0) {
- dev_err(dev, "Failed to enable measurement mode: %d\n", ret);
- return ret;
- }
+ ret = adxl345_powerup(data->regmap);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable measurement mode\n");
ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap);
if (ret < 0)
@@ -270,7 +280,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(adxl345_core_probe);
+EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345);
MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver");
diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c
index a431cba216e6..098cd83f95b2 100644
--- a/drivers/iio/accel/adxl345_i2c.c
+++ b/drivers/iio/accel/adxl345_i2c.c
@@ -19,23 +19,15 @@ static const struct regmap_config adxl345_i2c_regmap_config = {
.val_bits = 8,
};
-static int adxl345_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adxl345_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
- if (!id)
- return -ENODEV;
-
regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
- PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n");
- return adxl345_core_probe(&client->dev, regmap, id->driver_data,
- id->name);
+ return adxl345_core_probe(&client->dev, regmap);
}
static const struct i2c_device_id adxl345_i2c_id[] = {
@@ -43,28 +35,33 @@ static const struct i2c_device_id adxl345_i2c_id[] = {
{ "adxl375", ADXL375 },
{ }
};
-
MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id);
static const struct of_device_id adxl345_of_match[] = {
- { .compatible = "adi,adxl345" },
- { .compatible = "adi,adxl375" },
- { },
+ { .compatible = "adi,adxl345", .data = (const void *)ADXL345 },
+ { .compatible = "adi,adxl375", .data = (const void *)ADXL375 },
+ { }
};
-
MODULE_DEVICE_TABLE(of, adxl345_of_match);
+static const struct acpi_device_id adxl345_acpi_match[] = {
+ { "ADS0345", ADXL345 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match);
+
static struct i2c_driver adxl345_i2c_driver = {
.driver = {
.name = "adxl345_i2c",
.of_match_table = adxl345_of_match,
+ .acpi_match_table = adxl345_acpi_match,
},
- .probe = adxl345_i2c_probe,
+ .probe_new = adxl345_i2c_probe,
.id_table = adxl345_i2c_id,
};
-
module_i2c_driver(adxl345_i2c_driver);
MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer I2C driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL345);
diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c
index ea559ac2e87d..aaade5808657 100644
--- a/drivers/iio/accel/adxl345_spi.c
+++ b/drivers/iio/accel/adxl345_spi.c
@@ -22,24 +22,18 @@ static const struct regmap_config adxl345_spi_regmap_config = {
static int adxl345_spi_probe(struct spi_device *spi)
{
- const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap *regmap;
/* Bail out if max_speed_hz exceeds 5 MHz */
- if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) {
- dev_err(&spi->dev, "SPI CLK, %d Hz exceeds 5 MHz\n",
- spi->max_speed_hz);
- return -EINVAL;
- }
+ if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ)
+ return dev_err_probe(&spi->dev, -EINVAL, "SPI CLK, %d Hz exceeds 5 MHz\n",
+ spi->max_speed_hz);
regmap = devm_regmap_init_spi(spi, &adxl345_spi_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
- PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n");
- return adxl345_core_probe(&spi->dev, regmap, id->driver_data, id->name);
+ return adxl345_core_probe(&spi->dev, regmap);
}
static const struct spi_device_id adxl345_spi_id[] = {
@@ -47,28 +41,33 @@ static const struct spi_device_id adxl345_spi_id[] = {
{ "adxl375", ADXL375 },
{ }
};
-
MODULE_DEVICE_TABLE(spi, adxl345_spi_id);
static const struct of_device_id adxl345_of_match[] = {
- { .compatible = "adi,adxl345" },
- { .compatible = "adi,adxl375" },
- { },
+ { .compatible = "adi,adxl345", .data = (const void *)ADXL345 },
+ { .compatible = "adi,adxl375", .data = (const void *)ADXL375 },
+ { }
};
-
MODULE_DEVICE_TABLE(of, adxl345_of_match);
+static const struct acpi_device_id adxl345_acpi_match[] = {
+ { "ADS0345", ADXL345 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match);
+
static struct spi_driver adxl345_spi_driver = {
.driver = {
.name = "adxl345_spi",
.of_match_table = adxl345_of_match,
+ .acpi_match_table = adxl345_acpi_match,
},
.probe = adxl345_spi_probe,
.id_table = adxl345_spi_id,
};
-
module_spi_driver(adxl345_spi_driver);
MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL345);
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 4f485909f459..e9c10c8c32f0 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -20,6 +20,8 @@
#include <linux/mod_devicetable.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
+#include <linux/units.h>
+
#include <asm/unaligned.h>
#include "adxl355.h"
@@ -60,9 +62,6 @@
#define ADXL355_PARTID_VAL 0xED
#define ADXL355_RESET_CODE 0x52
-#define MEGA 1000000UL
-#define TERA 1000000000000ULL
-
static const struct regmap_range adxl355_read_reg_range[] = {
regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG),
regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG),
@@ -72,7 +71,7 @@ const struct regmap_access_table adxl355_readable_regs_tbl = {
.yes_ranges = adxl355_read_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range),
};
-EXPORT_SYMBOL_GPL(adxl355_readable_regs_tbl);
+EXPORT_SYMBOL_NS_GPL(adxl355_readable_regs_tbl, IIO_ADXL355);
static const struct regmap_range adxl355_write_reg_range[] = {
regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG),
@@ -82,7 +81,7 @@ const struct regmap_access_table adxl355_writeable_regs_tbl = {
.yes_ranges = adxl355_write_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range),
};
-EXPORT_SYMBOL_GPL(adxl355_writeable_regs_tbl);
+EXPORT_SYMBOL_NS_GPL(adxl355_writeable_regs_tbl, IIO_ADXL355);
enum adxl355_op_mode {
ADXL355_MEASUREMENT,
@@ -758,7 +757,7 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(adxl355_core_probe);
+EXPORT_SYMBOL_NS_GPL(adxl355_core_probe, IIO_ADXL355);
MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>");
MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver");
diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c
index 5a987bda9060..f67d57921c81 100644
--- a/drivers/iio/accel/adxl355_i2c.c
+++ b/drivers/iio/accel/adxl355_i2c.c
@@ -60,3 +60,4 @@ module_i2c_driver(adxl355_i2c_driver);
MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>");
MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer I2C driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL355);
diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c
index fb225aeb56e3..5fe986ae03f6 100644
--- a/drivers/iio/accel/adxl355_spi.c
+++ b/drivers/iio/accel/adxl355_spi.c
@@ -63,3 +63,4 @@ module_spi_driver(adxl355_spi_driver);
MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>");
MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL355);
diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c
new file mode 100644
index 000000000000..62960134ea19
--- /dev/null
+++ b/drivers/iio/accel/adxl367.c
@@ -0,0 +1,1588 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Analog Devices, Inc.
+ * Author: Cosmin Tanislav <cosmin.tanislav@analog.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include "adxl367.h"
+
+#define ADXL367_REG_DEVID 0x00
+#define ADXL367_DEVID_AD 0xAD
+
+#define ADXL367_REG_STATUS 0x0B
+#define ADXL367_STATUS_INACT_MASK BIT(5)
+#define ADXL367_STATUS_ACT_MASK BIT(4)
+#define ADXL367_STATUS_FIFO_FULL_MASK BIT(2)
+
+#define ADXL367_FIFO_ENT_H_MASK GENMASK(1, 0)
+
+#define ADXL367_REG_X_DATA_H 0x0E
+#define ADXL367_REG_Y_DATA_H 0x10
+#define ADXL367_REG_Z_DATA_H 0x12
+#define ADXL367_REG_TEMP_DATA_H 0x14
+#define ADXL367_REG_EX_ADC_DATA_H 0x16
+#define ADXL367_DATA_MASK GENMASK(15, 2)
+
+#define ADXL367_TEMP_25C 165
+#define ADXL367_TEMP_PER_C 54
+
+#define ADXL367_VOLTAGE_OFFSET 8192
+#define ADXL367_VOLTAGE_MAX_MV 1000
+#define ADXL367_VOLTAGE_MAX_RAW GENMASK(13, 0)
+
+#define ADXL367_REG_RESET 0x1F
+#define ADXL367_RESET_CODE 0x52
+
+#define ADXL367_REG_THRESH_ACT_H 0x20
+#define ADXL367_REG_THRESH_INACT_H 0x23
+#define ADXL367_THRESH_MAX GENMASK(12, 0)
+#define ADXL367_THRESH_VAL_H_MASK GENMASK(12, 6)
+#define ADXL367_THRESH_H_MASK GENMASK(6, 0)
+#define ADXL367_THRESH_VAL_L_MASK GENMASK(5, 0)
+#define ADXL367_THRESH_L_MASK GENMASK(7, 2)
+
+#define ADXL367_REG_TIME_ACT 0x22
+#define ADXL367_REG_TIME_INACT_H 0x25
+#define ADXL367_TIME_ACT_MAX GENMASK(7, 0)
+#define ADXL367_TIME_INACT_MAX GENMASK(15, 0)
+#define ADXL367_TIME_INACT_VAL_H_MASK GENMASK(15, 8)
+#define ADXL367_TIME_INACT_H_MASK GENMASK(7, 0)
+#define ADXL367_TIME_INACT_VAL_L_MASK GENMASK(7, 0)
+#define ADXL367_TIME_INACT_L_MASK GENMASK(7, 0)
+
+#define ADXL367_REG_ACT_INACT_CTL 0x27
+#define ADXL367_ACT_EN_MASK GENMASK(1, 0)
+#define ADXL367_ACT_LINKLOOP_MASK GENMASK(5, 4)
+
+#define ADXL367_REG_FIFO_CTL 0x28
+#define ADXL367_FIFO_CTL_FORMAT_MASK GENMASK(6, 3)
+#define ADXL367_FIFO_CTL_MODE_MASK GENMASK(1, 0)
+
+#define ADXL367_REG_FIFO_SAMPLES 0x29
+#define ADXL367_FIFO_SIZE 512
+#define ADXL367_FIFO_MAX_WATERMARK 511
+
+#define ADXL367_SAMPLES_VAL_H_MASK BIT(8)
+#define ADXL367_SAMPLES_H_MASK BIT(2)
+#define ADXL367_SAMPLES_VAL_L_MASK GENMASK(7, 0)
+#define ADXL367_SAMPLES_L_MASK GENMASK(7, 0)
+
+#define ADXL367_REG_INT1_MAP 0x2A
+#define ADXL367_INT_INACT_MASK BIT(5)
+#define ADXL367_INT_ACT_MASK BIT(4)
+#define ADXL367_INT_FIFO_WATERMARK_MASK BIT(2)
+
+#define ADXL367_REG_FILTER_CTL 0x2C
+#define ADXL367_FILTER_CTL_RANGE_MASK GENMASK(7, 6)
+#define ADXL367_2G_RANGE_1G 4095
+#define ADXL367_2G_RANGE_100MG 409
+#define ADXL367_FILTER_CTL_ODR_MASK GENMASK(2, 0)
+
+#define ADXL367_REG_POWER_CTL 0x2D
+#define ADXL367_POWER_CTL_MODE_MASK GENMASK(1, 0)
+
+#define ADXL367_REG_ADC_CTL 0x3C
+#define ADXL367_REG_TEMP_CTL 0x3D
+#define ADXL367_ADC_EN_MASK BIT(0)
+
+enum adxl367_range {
+ ADXL367_2G_RANGE,
+ ADXL367_4G_RANGE,
+ ADXL367_8G_RANGE,
+};
+
+enum adxl367_fifo_mode {
+ ADXL367_FIFO_MODE_DISABLED = 0b00,
+ ADXL367_FIFO_MODE_STREAM = 0b10,
+};
+
+enum adxl367_fifo_format {
+ ADXL367_FIFO_FORMAT_XYZ,
+ ADXL367_FIFO_FORMAT_X,
+ ADXL367_FIFO_FORMAT_Y,
+ ADXL367_FIFO_FORMAT_Z,
+ ADXL367_FIFO_FORMAT_XYZT,
+ ADXL367_FIFO_FORMAT_XT,
+ ADXL367_FIFO_FORMAT_YT,
+ ADXL367_FIFO_FORMAT_ZT,
+ ADXL367_FIFO_FORMAT_XYZA,
+ ADXL367_FIFO_FORMAT_XA,
+ ADXL367_FIFO_FORMAT_YA,
+ ADXL367_FIFO_FORMAT_ZA,
+};
+
+enum adxl367_op_mode {
+ ADXL367_OP_STANDBY = 0b00,
+ ADXL367_OP_MEASURE = 0b10,
+};
+
+enum adxl367_act_proc_mode {
+ ADXL367_LOOPED = 0b11,
+};
+
+enum adxl367_act_en_mode {
+ ADXL367_ACT_DISABLED = 0b00,
+ ADCL367_ACT_REF_ENABLED = 0b11,
+};
+
+enum adxl367_activity_type {
+ ADXL367_ACTIVITY,
+ ADXL367_INACTIVITY,
+};
+
+enum adxl367_odr {
+ ADXL367_ODR_12P5HZ,
+ ADXL367_ODR_25HZ,
+ ADXL367_ODR_50HZ,
+ ADXL367_ODR_100HZ,
+ ADXL367_ODR_200HZ,
+ ADXL367_ODR_400HZ,
+};
+
+struct adxl367_state {
+ const struct adxl367_ops *ops;
+ void *context;
+
+ struct device *dev;
+ struct regmap *regmap;
+
+ struct regulator_bulk_data regulators[2];
+
+ /*
+ * Synchronize access to members of driver state, and ensure atomicity
+ * of consecutive regmap operations.
+ */
+ struct mutex lock;
+
+ enum adxl367_odr odr;
+ enum adxl367_range range;
+
+ unsigned int act_threshold;
+ unsigned int act_time_ms;
+ unsigned int inact_threshold;
+ unsigned int inact_time_ms;
+
+ unsigned int fifo_set_size;
+ unsigned int fifo_watermark;
+
+ __be16 fifo_buf[ADXL367_FIFO_SIZE] ____cacheline_aligned;
+ __be16 sample_buf;
+ u8 act_threshold_buf[2];
+ u8 inact_time_buf[2];
+ u8 status_buf[3];
+};
+
+static const unsigned int adxl367_threshold_h_reg_tbl[] = {
+ [ADXL367_ACTIVITY] = ADXL367_REG_THRESH_ACT_H,
+ [ADXL367_INACTIVITY] = ADXL367_REG_THRESH_INACT_H,
+};
+
+static const unsigned int adxl367_act_en_shift_tbl[] = {
+ [ADXL367_ACTIVITY] = 0,
+ [ADXL367_INACTIVITY] = 2,
+};
+
+static const unsigned int adxl367_act_int_mask_tbl[] = {
+ [ADXL367_ACTIVITY] = ADXL367_INT_ACT_MASK,
+ [ADXL367_INACTIVITY] = ADXL367_INT_INACT_MASK,
+};
+
+static const int adxl367_samp_freq_tbl[][2] = {
+ [ADXL367_ODR_12P5HZ] = {12, 500000},
+ [ADXL367_ODR_25HZ] = {25, 0},
+ [ADXL367_ODR_50HZ] = {50, 0},
+ [ADXL367_ODR_100HZ] = {100, 0},
+ [ADXL367_ODR_200HZ] = {200, 0},
+ [ADXL367_ODR_400HZ] = {400, 0},
+};
+
+/* (g * 2) * 9.80665 * 1000000 / (2^14 - 1) */
+static const int adxl367_range_scale_tbl[][2] = {
+ [ADXL367_2G_RANGE] = {0, 2394347},
+ [ADXL367_4G_RANGE] = {0, 4788695},
+ [ADXL367_8G_RANGE] = {0, 9577391},
+};
+
+static const int adxl367_range_scale_factor_tbl[] = {
+ [ADXL367_2G_RANGE] = 1,
+ [ADXL367_4G_RANGE] = 2,
+ [ADXL367_8G_RANGE] = 4,
+};
+
+enum {
+ ADXL367_X_CHANNEL_INDEX,
+ ADXL367_Y_CHANNEL_INDEX,
+ ADXL367_Z_CHANNEL_INDEX,
+ ADXL367_TEMP_CHANNEL_INDEX,
+ ADXL367_EX_ADC_CHANNEL_INDEX
+};
+
+#define ADXL367_X_CHANNEL_MASK BIT(ADXL367_X_CHANNEL_INDEX)
+#define ADXL367_Y_CHANNEL_MASK BIT(ADXL367_Y_CHANNEL_INDEX)
+#define ADXL367_Z_CHANNEL_MASK BIT(ADXL367_Z_CHANNEL_INDEX)
+#define ADXL367_TEMP_CHANNEL_MASK BIT(ADXL367_TEMP_CHANNEL_INDEX)
+#define ADXL367_EX_ADC_CHANNEL_MASK BIT(ADXL367_EX_ADC_CHANNEL_INDEX)
+
+static const enum adxl367_fifo_format adxl367_fifo_formats[] = {
+ ADXL367_FIFO_FORMAT_X,
+ ADXL367_FIFO_FORMAT_Y,
+ ADXL367_FIFO_FORMAT_Z,
+ ADXL367_FIFO_FORMAT_XT,
+ ADXL367_FIFO_FORMAT_YT,
+ ADXL367_FIFO_FORMAT_ZT,
+ ADXL367_FIFO_FORMAT_XA,
+ ADXL367_FIFO_FORMAT_YA,
+ ADXL367_FIFO_FORMAT_ZA,
+ ADXL367_FIFO_FORMAT_XYZ,
+ ADXL367_FIFO_FORMAT_XYZT,
+ ADXL367_FIFO_FORMAT_XYZA,
+};
+
+static const unsigned long adxl367_channel_masks[] = {
+ ADXL367_X_CHANNEL_MASK,
+ ADXL367_Y_CHANNEL_MASK,
+ ADXL367_Z_CHANNEL_MASK,
+ ADXL367_X_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK,
+ ADXL367_Y_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK,
+ ADXL367_Z_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK,
+ ADXL367_X_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK,
+ ADXL367_Y_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK,
+ ADXL367_Z_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK,
+ ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK,
+ ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK |
+ ADXL367_TEMP_CHANNEL_MASK,
+ ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK |
+ ADXL367_EX_ADC_CHANNEL_MASK,
+ 0,
+};
+
+static int adxl367_set_measure_en(struct adxl367_state *st, bool en)
+{
+ enum adxl367_op_mode op_mode = en ? ADXL367_OP_MEASURE
+ : ADXL367_OP_STANDBY;
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, ADXL367_REG_POWER_CTL,
+ ADXL367_POWER_CTL_MODE_MASK,
+ FIELD_PREP(ADXL367_POWER_CTL_MODE_MASK,
+ op_mode));
+ if (ret)
+ return ret;
+
+ /*
+ * Wait for acceleration output to settle after entering
+ * measure mode.
+ */
+ if (en)
+ msleep(100);
+
+ return 0;
+}
+
+static void adxl367_scale_act_thresholds(struct adxl367_state *st,
+ enum adxl367_range old_range,
+ enum adxl367_range new_range)
+{
+ st->act_threshold = st->act_threshold
+ * adxl367_range_scale_factor_tbl[old_range]
+ / adxl367_range_scale_factor_tbl[new_range];
+ st->inact_threshold = st->inact_threshold
+ * adxl367_range_scale_factor_tbl[old_range]
+ / adxl367_range_scale_factor_tbl[new_range];
+}
+
+static int _adxl367_set_act_threshold(struct adxl367_state *st,
+ enum adxl367_activity_type act,
+ unsigned int threshold)
+{
+ u8 reg = adxl367_threshold_h_reg_tbl[act];
+ int ret;
+
+ if (threshold > ADXL367_THRESH_MAX)
+ return -EINVAL;
+
+ st->act_threshold_buf[0] = FIELD_PREP(ADXL367_THRESH_H_MASK,
+ FIELD_GET(ADXL367_THRESH_VAL_H_MASK,
+ threshold));
+ st->act_threshold_buf[1] = FIELD_PREP(ADXL367_THRESH_L_MASK,
+ FIELD_GET(ADXL367_THRESH_VAL_L_MASK,
+ threshold));
+
+ ret = regmap_bulk_write(st->regmap, reg, st->act_threshold_buf,
+ sizeof(st->act_threshold_buf));
+ if (ret)
+ return ret;
+
+ if (act == ADXL367_ACTIVITY)
+ st->act_threshold = threshold;
+ else
+ st->inact_threshold = threshold;
+
+ return 0;
+}
+
+static int adxl367_set_act_threshold(struct adxl367_state *st,
+ enum adxl367_activity_type act,
+ unsigned int threshold)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = _adxl367_set_act_threshold(st, act, threshold);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int adxl367_set_act_proc_mode(struct adxl367_state *st,
+ enum adxl367_act_proc_mode mode)
+{
+ return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL,
+ ADXL367_ACT_LINKLOOP_MASK,
+ FIELD_PREP(ADXL367_ACT_LINKLOOP_MASK,
+ mode));
+}
+
+static int adxl367_set_act_interrupt_en(struct adxl367_state *st,
+ enum adxl367_activity_type act,
+ bool en)
+{
+ unsigned int mask = adxl367_act_int_mask_tbl[act];
+
+ return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP,
+ mask, en ? mask : 0);
+}
+
+static int adxl367_get_act_interrupt_en(struct adxl367_state *st,
+ enum adxl367_activity_type act,
+ bool *en)
+{
+ unsigned int mask = adxl367_act_int_mask_tbl[act];
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val);
+ if (ret)
+ return ret;
+
+ *en = !!(val & mask);
+
+ return 0;
+}
+
+static int adxl367_set_act_en(struct adxl367_state *st,
+ enum adxl367_activity_type act,
+ enum adxl367_act_en_mode en)
+{
+ unsigned int ctl_shift = adxl367_act_en_shift_tbl[act];
+
+ return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL,
+ ADXL367_ACT_EN_MASK << ctl_shift,
+ en << ctl_shift);
+}
+
+static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st,
+ bool en)
+{
+ return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP,
+ ADXL367_INT_FIFO_WATERMARK_MASK,
+ en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0);
+}
+
+static int adxl367_get_fifo_mode(struct adxl367_state *st,
+ enum adxl367_fifo_mode *fifo_mode)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADXL367_REG_FIFO_CTL, &val);
+ if (ret)
+ return ret;
+
+ *fifo_mode = FIELD_GET(ADXL367_FIFO_CTL_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adxl367_set_fifo_mode(struct adxl367_state *st,
+ enum adxl367_fifo_mode fifo_mode)
+{
+ return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL,
+ ADXL367_FIFO_CTL_MODE_MASK,
+ FIELD_PREP(ADXL367_FIFO_CTL_MODE_MASK,
+ fifo_mode));
+}
+
+static int adxl367_set_fifo_format(struct adxl367_state *st,
+ enum adxl367_fifo_format fifo_format)
+{
+ return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL,
+ ADXL367_FIFO_CTL_FORMAT_MASK,
+ FIELD_PREP(ADXL367_FIFO_CTL_FORMAT_MASK,
+ fifo_format));
+}
+
+static int adxl367_set_fifo_samples(struct adxl367_state *st,
+ unsigned int fifo_watermark,
+ unsigned int fifo_set_size)
+{
+ unsigned int fifo_samples = fifo_watermark * fifo_set_size;
+ unsigned int fifo_samples_h, fifo_samples_l;
+ int ret;
+
+ if (fifo_samples > ADXL367_FIFO_MAX_WATERMARK)
+ fifo_samples = ADXL367_FIFO_MAX_WATERMARK;
+
+ if (fifo_set_size == 0)
+ return 0;
+
+ fifo_samples /= fifo_set_size;
+
+ fifo_samples_h = FIELD_PREP(ADXL367_SAMPLES_H_MASK,
+ FIELD_GET(ADXL367_SAMPLES_VAL_H_MASK,
+ fifo_samples));
+ fifo_samples_l = FIELD_PREP(ADXL367_SAMPLES_L_MASK,
+ FIELD_GET(ADXL367_SAMPLES_VAL_L_MASK,
+ fifo_samples));
+
+ ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL,
+ ADXL367_SAMPLES_H_MASK, fifo_samples_h);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES,
+ ADXL367_SAMPLES_L_MASK, fifo_samples_l);
+}
+
+static int adxl367_set_fifo_set_size(struct adxl367_state *st,
+ unsigned int fifo_set_size)
+{
+ int ret;
+
+ ret = adxl367_set_fifo_samples(st, st->fifo_watermark, fifo_set_size);
+ if (ret)
+ return ret;
+
+ st->fifo_set_size = fifo_set_size;
+
+ return 0;
+}
+
+static int adxl367_set_fifo_watermark(struct adxl367_state *st,
+ unsigned int fifo_watermark)
+{
+ int ret;
+
+ ret = adxl367_set_fifo_samples(st, fifo_watermark, st->fifo_set_size);
+ if (ret)
+ return ret;
+
+ st->fifo_watermark = fifo_watermark;
+
+ return 0;
+}
+
+static int adxl367_set_range(struct iio_dev *indio_dev,
+ enum adxl367_range range)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
+ ADXL367_FILTER_CTL_RANGE_MASK,
+ FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK,
+ range));
+ if (ret)
+ goto out;
+
+ adxl367_scale_act_thresholds(st, st->range, range);
+
+ /* Activity thresholds depend on range */
+ ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
+ st->act_threshold);
+ if (ret)
+ goto out;
+
+ ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
+ st->inact_threshold);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+ if (ret)
+ goto out;
+
+ st->range = range;
+
+out:
+ mutex_unlock(&st->lock);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms)
+{
+ int freq_hz = adxl367_samp_freq_tbl[st->odr][0];
+ int freq_microhz = adxl367_samp_freq_tbl[st->odr][1];
+ /* Scale to decihertz to prevent precision loss in 12.5Hz case. */
+ int freq_dhz = freq_hz * 10 + freq_microhz / 100000;
+
+ return DIV_ROUND_CLOSEST(ms * freq_dhz, 10000);
+}
+
+static int _adxl367_set_act_time_ms(struct adxl367_state *st, unsigned int ms)
+{
+ unsigned int val = adxl367_time_ms_to_samples(st, ms);
+ int ret;
+
+ if (val > ADXL367_TIME_ACT_MAX)
+ val = ADXL367_TIME_ACT_MAX;
+
+ ret = regmap_write(st->regmap, ADXL367_REG_TIME_ACT, val);
+ if (ret)
+ return ret;
+
+ st->act_time_ms = ms;
+
+ return 0;
+}
+
+static int _adxl367_set_inact_time_ms(struct adxl367_state *st, unsigned int ms)
+{
+ unsigned int val = adxl367_time_ms_to_samples(st, ms);
+ int ret;
+
+ if (val > ADXL367_TIME_INACT_MAX)
+ val = ADXL367_TIME_INACT_MAX;
+
+ st->inact_time_buf[0] = FIELD_PREP(ADXL367_TIME_INACT_H_MASK,
+ FIELD_GET(ADXL367_TIME_INACT_VAL_H_MASK,
+ val));
+ st->inact_time_buf[1] = FIELD_PREP(ADXL367_TIME_INACT_L_MASK,
+ FIELD_GET(ADXL367_TIME_INACT_VAL_L_MASK,
+ val));
+
+ ret = regmap_bulk_write(st->regmap, ADXL367_REG_TIME_INACT_H,
+ st->inact_time_buf, sizeof(st->inact_time_buf));
+ if (ret)
+ return ret;
+
+ st->inact_time_ms = ms;
+
+ return 0;
+}
+
+static int adxl367_set_act_time_ms(struct adxl367_state *st,
+ enum adxl367_activity_type act,
+ unsigned int ms)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ if (act == ADXL367_ACTIVITY)
+ ret = _adxl367_set_act_time_ms(st, ms);
+ else
+ ret = _adxl367_set_inact_time_ms(st, ms);
+
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
+{
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
+ ADXL367_FILTER_CTL_ODR_MASK,
+ FIELD_PREP(ADXL367_FILTER_CTL_ODR_MASK,
+ odr));
+ if (ret)
+ return ret;
+
+ /* Activity timers depend on ODR */
+ ret = _adxl367_set_act_time_ms(st, st->act_time_ms);
+ if (ret)
+ return ret;
+
+ ret = _adxl367_set_inact_time_ms(st, st->inact_time_ms);
+ if (ret)
+ return ret;
+
+ st->odr = odr;
+
+ return 0;
+}
+
+static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = _adxl367_set_odr(st, odr);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg,
+ bool en)
+{
+ return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK,
+ en ? ADXL367_ADC_EN_MASK : 0);
+}
+
+static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st,
+ unsigned int reg, bool en)
+{
+ int ret;
+
+ switch (reg) {
+ case ADXL367_REG_TEMP_DATA_H:
+ ret = adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en);
+ break;
+ case ADXL367_REG_EX_ADC_DATA_H:
+ ret = adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en);
+ break;
+ default:
+ return 0;
+ }
+
+ if (ret)
+ return ret;
+
+ if (en)
+ msleep(100);
+
+ return 0;
+}
+
+static int adxl367_set_temp_adc_mask_en(struct adxl367_state *st,
+ const unsigned long *active_scan_mask,
+ bool en)
+{
+ if (*active_scan_mask & ADXL367_TEMP_CHANNEL_MASK)
+ return adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en);
+ else if (*active_scan_mask & ADXL367_EX_ADC_CHANNEL_MASK)
+ return adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en);
+
+ return 0;
+}
+
+static int adxl367_find_odr(struct adxl367_state *st, int val, int val2,
+ enum adxl367_odr *odr)
+{
+ size_t size = ARRAY_SIZE(adxl367_samp_freq_tbl);
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (val == adxl367_samp_freq_tbl[i][0] &&
+ val2 == adxl367_samp_freq_tbl[i][1])
+ break;
+
+ if (i == size)
+ return -EINVAL;
+
+ *odr = i;
+
+ return 0;
+}
+
+static int adxl367_find_range(struct adxl367_state *st, int val, int val2,
+ enum adxl367_range *range)
+{
+ size_t size = ARRAY_SIZE(adxl367_range_scale_tbl);
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (val == adxl367_range_scale_tbl[i][0] &&
+ val2 == adxl367_range_scale_tbl[i][1])
+ break;
+
+ if (i == size)
+ return -EINVAL;
+
+ *range = i;
+
+ return 0;
+}
+
+static int adxl367_read_sample(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ u16 sample;
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
+ if (ret)
+ goto out;
+
+ ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
+ sizeof(st->sample_buf));
+ if (ret)
+ goto out;
+
+ sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
+ *val = sign_extend32(sample, chan->scan_type.realbits - 1);
+
+ ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
+
+out:
+ mutex_unlock(&st->lock);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret ?: IIO_VAL_INT;
+}
+
+static int adxl367_get_status(struct adxl367_state *st, u8 *status,
+ u16 *fifo_entries)
+{
+ int ret;
+
+ /* Read STATUS, FIFO_ENT_L and FIFO_ENT_H */
+ ret = regmap_bulk_read(st->regmap, ADXL367_REG_STATUS,
+ st->status_buf, sizeof(st->status_buf));
+ if (ret)
+ return ret;
+
+ st->status_buf[2] &= ADXL367_FIFO_ENT_H_MASK;
+
+ *status = st->status_buf[0];
+ *fifo_entries = get_unaligned_le16(&st->status_buf[1]);
+
+ return 0;
+}
+
+static bool adxl367_push_event(struct iio_dev *indio_dev, u8 status)
+{
+ unsigned int ev_dir;
+
+ if (FIELD_GET(ADXL367_STATUS_ACT_MASK, status))
+ ev_dir = IIO_EV_DIR_RISING;
+ else if (FIELD_GET(ADXL367_STATUS_INACT_MASK, status))
+ ev_dir = IIO_EV_DIR_FALLING;
+ else
+ return false;
+
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_THRESH, ev_dir),
+ iio_get_time_ns(indio_dev));
+
+ return true;
+}
+
+static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status,
+ u16 fifo_entries)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+ int i;
+
+ if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status))
+ return false;
+
+ fifo_entries -= fifo_entries % st->fifo_set_size;
+
+ ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries);
+ if (ret) {
+ dev_err(st->dev, "Failed to read FIFO: %d\n", ret);
+ return true;
+ }
+
+ for (i = 0; i < fifo_entries; i += st->fifo_set_size)
+ iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
+
+ return true;
+}
+
+static irqreturn_t adxl367_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct adxl367_state *st = iio_priv(indio_dev);
+ u16 fifo_entries;
+ bool handled;
+ u8 status;
+ int ret;
+
+ ret = adxl367_get_status(st, &status, &fifo_entries);
+ if (ret)
+ return IRQ_NONE;
+
+ handled = adxl367_push_event(indio_dev, status);
+ handled |= adxl367_push_fifo_data(indio_dev, status, fifo_entries);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int adxl367_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int adxl367_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return adxl367_read_sample(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ mutex_lock(&st->lock);
+ *val = adxl367_range_scale_tbl[st->range][0];
+ *val2 = adxl367_range_scale_tbl[st->range][1];
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_TEMP:
+ *val = 1000;
+ *val2 = ADXL367_TEMP_PER_C;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_VOLTAGE:
+ *val = ADXL367_VOLTAGE_MAX_MV;
+ *val2 = ADXL367_VOLTAGE_MAX_RAW;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = 25 * ADXL367_TEMP_PER_C - ADXL367_TEMP_25C;
+ return IIO_VAL_INT;
+ case IIO_VOLTAGE:
+ *val = ADXL367_VOLTAGE_OFFSET;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ mutex_lock(&st->lock);
+ *val = adxl367_samp_freq_tbl[st->odr][0];
+ *val2 = adxl367_samp_freq_tbl[st->odr][1];
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl367_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ enum adxl367_odr odr;
+
+ ret = adxl367_find_odr(st, val, val2, &odr);
+ if (ret)
+ return ret;
+
+ return adxl367_set_odr(indio_dev, odr);
+ }
+ case IIO_CHAN_INFO_SCALE: {
+ enum adxl367_range range;
+
+ ret = adxl367_find_range(st, val, val2, &range);
+ if (ret)
+ return ret;
+
+ return adxl367_set_range(indio_dev, range);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl367_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int adxl367_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ *vals = (int *)adxl367_range_scale_tbl;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(adxl367_range_scale_tbl) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (int *)adxl367_samp_freq_tbl;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(adxl367_samp_freq_tbl) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl367_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 adxl367_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE: {
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&st->lock);
+ *val = st->act_threshold;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&st->lock);
+ *val = st->inact_threshold;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ }
+ case IIO_EV_INFO_PERIOD:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&st->lock);
+ *val = st->act_time_ms;
+ mutex_unlock(&st->lock);
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&st->lock);
+ *val = st->inact_time_ms;
+ mutex_unlock(&st->lock);
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl367_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 adxl367_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 0)
+ return -EINVAL;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return adxl367_set_act_threshold(st, ADXL367_ACTIVITY, val);
+ case IIO_EV_DIR_FALLING:
+ return adxl367_set_act_threshold(st, ADXL367_INACTIVITY, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_PERIOD:
+ if (val < 0)
+ return -EINVAL;
+
+ val = val * 1000 + DIV_ROUND_UP(val2, 1000);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return adxl367_set_act_time_ms(st, ADXL367_ACTIVITY, val);
+ case IIO_EV_DIR_FALLING:
+ return adxl367_set_act_time_ms(st, ADXL367_INACTIVITY, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl367_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 adxl367_state *st = iio_priv(indio_dev);
+ bool en;
+ int ret;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = adxl367_get_act_interrupt_en(st, ADXL367_ACTIVITY, &en);
+ return ret ?: en;
+ case IIO_EV_DIR_FALLING:
+ ret = adxl367_get_act_interrupt_en(st, ADXL367_INACTIVITY, &en);
+ return ret ?: en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl367_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ enum adxl367_activity_type act;
+ int ret;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ act = ADXL367_ACTIVITY;
+ break;
+ case IIO_EV_DIR_FALLING:
+ act = ADXL367_INACTIVITY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_act_interrupt_en(st, act, state);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
+ : ADXL367_ACT_DISABLED);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static ssize_t adxl367_get_fifo_enabled(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev));
+ enum adxl367_fifo_mode fifo_mode;
+ int ret;
+
+ ret = adxl367_get_fifo_mode(st, &fifo_mode);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", fifo_mode != ADXL367_FIFO_MODE_DISABLED);
+}
+
+static ssize_t adxl367_get_fifo_watermark(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned int fifo_watermark;
+
+ mutex_lock(&st->lock);
+ fifo_watermark = st->fifo_watermark;
+ mutex_unlock(&st->lock);
+
+ return sysfs_emit(buf, "%d\n", fifo_watermark);
+}
+
+static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
+static IIO_CONST_ATTR(hwfifo_watermark_max,
+ __stringify(ADXL367_FIFO_MAX_WATERMARK));
+static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
+ adxl367_get_fifo_watermark, NULL, 0);
+static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
+ adxl367_get_fifo_enabled, NULL, 0);
+
+static const struct attribute *adxl367_fifo_attributes[] = {
+ &iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
+ &iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
+ &iio_dev_attr_hwfifo_watermark.dev_attr.attr,
+ &iio_dev_attr_hwfifo_enabled.dev_attr.attr,
+ NULL,
+};
+
+static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (val > ADXL367_FIFO_MAX_WATERMARK)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_watermark(st, val);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask,
+ enum adxl367_fifo_format *fifo_format)
+{
+ size_t size = ARRAY_SIZE(adxl367_fifo_formats);
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (*scan_mask == adxl367_channel_masks[i])
+ break;
+
+ if (i == size)
+ return false;
+
+ *fifo_format = adxl367_fifo_formats[i];
+
+ return true;
+}
+
+static int adxl367_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ enum adxl367_fifo_format fifo_format;
+ unsigned int fifo_set_size;
+ int ret;
+
+ if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format))
+ return -EINVAL;
+
+ fifo_set_size = bitmap_weight(active_scan_mask, indio_dev->masklength);
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_format(st, fifo_format);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_set_size(st, fifo_set_size);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int adxl367_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
+ true);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_watermark_interrupt_en(st, true);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+
+out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int adxl367_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct adxl367_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = adxl367_set_measure_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_fifo_watermark_interrupt_en(st, false);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_measure_en(st, true);
+ if (ret)
+ goto out;
+
+ ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
+ false);
+
+out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops adxl367_buffer_ops = {
+ .postenable = adxl367_buffer_postenable,
+ .predisable = adxl367_buffer_predisable,
+};
+
+static const struct iio_info adxl367_info = {
+ .read_raw = adxl367_read_raw,
+ .write_raw = adxl367_write_raw,
+ .write_raw_get_fmt = adxl367_write_raw_get_fmt,
+ .read_avail = adxl367_read_avail,
+ .read_event_config = adxl367_read_event_config,
+ .write_event_config = adxl367_write_event_config,
+ .read_event_value = adxl367_read_event_value,
+ .write_event_value = adxl367_write_event_value,
+ .debugfs_reg_access = adxl367_reg_access,
+ .hwfifo_set_watermark = adxl367_set_watermark,
+ .update_scan_mode = adxl367_update_scan_mode,
+};
+
+static const struct iio_event_spec adxl367_events[] = {
+ {
+ .type = IIO_EV_TYPE_MAG_REFERENCED,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_MAG_REFERENCED,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+#define ADXL367_ACCEL_CHANNEL(index, reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = (reg), \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .event_spec = adxl367_events, \
+ .num_event_specs = ARRAY_SIZE(adxl367_events), \
+ .scan_index = (index), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 14, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADXL367_CHANNEL(index, reg, _type) { \
+ .type = (_type), \
+ .address = (reg), \
+ .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), \
+ .scan_index = (index), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 14, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec adxl367_channels[] = {
+ ADXL367_ACCEL_CHANNEL(ADXL367_X_CHANNEL_INDEX, ADXL367_REG_X_DATA_H, X),
+ ADXL367_ACCEL_CHANNEL(ADXL367_Y_CHANNEL_INDEX, ADXL367_REG_Y_DATA_H, Y),
+ ADXL367_ACCEL_CHANNEL(ADXL367_Z_CHANNEL_INDEX, ADXL367_REG_Z_DATA_H, Z),
+ ADXL367_CHANNEL(ADXL367_TEMP_CHANNEL_INDEX, ADXL367_REG_TEMP_DATA_H,
+ IIO_TEMP),
+ ADXL367_CHANNEL(ADXL367_EX_ADC_CHANNEL_INDEX, ADXL367_REG_EX_ADC_DATA_H,
+ IIO_VOLTAGE),
+};
+
+static int adxl367_verify_devid(struct adxl367_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(st->regmap, ADXL367_REG_DEVID, val,
+ val == ADXL367_DEVID_AD, 1000, 10000);
+ if (ret)
+ return dev_err_probe(st->dev, -ENODEV,
+ "Invalid dev id 0x%02X, expected 0x%02X\n",
+ val, ADXL367_DEVID_AD);
+
+ return 0;
+}
+
+static int adxl367_setup(struct adxl367_state *st)
+{
+ int ret;
+
+ ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
+ ADXL367_2G_RANGE_1G);
+ if (ret)
+ return ret;
+
+ ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
+ ADXL367_2G_RANGE_100MG);
+ if (ret)
+ return ret;
+
+ ret = adxl367_set_act_proc_mode(st, ADXL367_LOOPED);
+ if (ret)
+ return ret;
+
+ ret = _adxl367_set_odr(st, ADXL367_ODR_400HZ);
+ if (ret)
+ return ret;
+
+ ret = _adxl367_set_act_time_ms(st, 10);
+ if (ret)
+ return ret;
+
+ ret = _adxl367_set_inact_time_ms(st, 10000);
+ if (ret)
+ return ret;
+
+ return adxl367_set_measure_en(st, true);
+}
+
+static void adxl367_disable_regulators(void *data)
+{
+ struct adxl367_state *st = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
+}
+
+int adxl367_probe(struct device *dev, const struct adxl367_ops *ops,
+ void *context, struct regmap *regmap, int irq)
+{
+ struct iio_dev *indio_dev;
+ struct adxl367_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->dev = dev;
+ st->regmap = regmap;
+ st->context = context;
+ st->ops = ops;
+
+ mutex_init(&st->lock);
+
+ indio_dev->channels = adxl367_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adxl367_channels);
+ indio_dev->available_scan_masks = adxl367_channel_masks;
+ indio_dev->name = "adxl367";
+ indio_dev->info = &adxl367_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ st->regulators[0].supply = "vdd";
+ st->regulators[1].supply = "vddio";
+
+ ret = devm_regulator_bulk_get(st->dev, ARRAY_SIZE(st->regulators),
+ st->regulators);
+ if (ret)
+ return dev_err_probe(st->dev, ret,
+ "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators);
+ if (ret)
+ return dev_err_probe(st->dev, ret,
+ "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(st->dev, adxl367_disable_regulators, st);
+ if (ret)
+ return dev_err_probe(st->dev, ret,
+ "Failed to add regulators disable action\n");
+
+ ret = regmap_write(st->regmap, ADXL367_REG_RESET, ADXL367_RESET_CODE);
+ if (ret)
+ return ret;
+
+ ret = adxl367_verify_devid(st);
+ if (ret)
+ return ret;
+
+ ret = adxl367_setup(st);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_kfifo_buffer_setup_ext(st->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &adxl367_buffer_ops,
+ adxl367_fifo_attributes);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(st->dev, irq, NULL,
+ 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 devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(adxl367_probe, IIO_ADXL367);
+
+MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/adxl367.h b/drivers/iio/accel/adxl367.h
new file mode 100644
index 000000000000..4a42622149b1
--- /dev/null
+++ b/drivers/iio/accel/adxl367.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2021 Analog Devices, Inc.
+ * Author: Cosmin Tanislav <cosmin.tanislav@analog.com>
+ */
+
+#ifndef _ADXL367_H_
+#define _ADXL367_H_
+
+#include <linux/types.h>
+
+struct device;
+struct regmap;
+
+struct adxl367_ops {
+ int (*read_fifo)(void *context, __be16 *fifo_buf,
+ unsigned int fifo_entries);
+};
+
+int adxl367_probe(struct device *dev, const struct adxl367_ops *ops,
+ void *context, struct regmap *regmap, int irq);
+
+#endif /* _ADXL367_H_ */
diff --git a/drivers/iio/accel/adxl367_i2c.c b/drivers/iio/accel/adxl367_i2c.c
new file mode 100644
index 000000000000..3606efa25835
--- /dev/null
+++ b/drivers/iio/accel/adxl367_i2c.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Analog Devices, Inc.
+ * Author: Cosmin Tanislav <cosmin.tanislav@analog.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "adxl367.h"
+
+#define ADXL367_I2C_FIFO_DATA 0x42
+
+struct adxl367_i2c_state {
+ struct regmap *regmap;
+};
+
+static bool adxl367_readable_noinc_reg(struct device *dev, unsigned int reg)
+{
+ return reg == ADXL367_I2C_FIFO_DATA;
+}
+
+static int adxl367_i2c_read_fifo(void *context, __be16 *fifo_buf,
+ unsigned int fifo_entries)
+{
+ struct adxl367_i2c_state *st = context;
+
+ return regmap_noinc_read(st->regmap, ADXL367_I2C_FIFO_DATA, fifo_buf,
+ fifo_entries * sizeof(*fifo_buf));
+}
+
+static const struct regmap_config adxl367_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .readable_noinc_reg = adxl367_readable_noinc_reg,
+};
+
+static const struct adxl367_ops adxl367_i2c_ops = {
+ .read_fifo = adxl367_i2c_read_fifo,
+};
+
+static int adxl367_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adxl367_i2c_state *st;
+ struct regmap *regmap;
+
+ st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &adxl367_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ st->regmap = regmap;
+
+ return adxl367_probe(&client->dev, &adxl367_i2c_ops, st, regmap,
+ client->irq);
+}
+
+static const struct i2c_device_id adxl367_i2c_id[] = {
+ { "adxl367", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, adxl367_i2c_id);
+
+static const struct of_device_id adxl367_of_match[] = {
+ { .compatible = "adi,adxl367" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adxl367_of_match);
+
+static struct i2c_driver adxl367_i2c_driver = {
+ .driver = {
+ .name = "adxl367_i2c",
+ .of_match_table = adxl367_of_match,
+ },
+ .probe = adxl367_i2c_probe,
+ .id_table = adxl367_i2c_id,
+};
+
+module_i2c_driver(adxl367_i2c_driver);
+
+MODULE_IMPORT_NS(IIO_ADXL367);
+MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c
new file mode 100644
index 000000000000..26dfc821ebbe
--- /dev/null
+++ b/drivers/iio/accel/adxl367_spi.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Analog Devices, Inc.
+ * Author: Cosmin Tanislav <cosmin.tanislav@analog.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "adxl367.h"
+
+#define ADXL367_SPI_WRITE_COMMAND 0x0A
+#define ADXL367_SPI_READ_COMMAND 0x0B
+#define ADXL367_SPI_FIFO_COMMAND 0x0D
+
+struct adxl367_spi_state {
+ struct spi_device *spi;
+
+ struct spi_message reg_write_msg;
+ struct spi_transfer reg_write_xfer[2];
+
+ struct spi_message reg_read_msg;
+ struct spi_transfer reg_read_xfer[2];
+
+ struct spi_message fifo_msg;
+ struct spi_transfer fifo_xfer[2];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 reg_write_tx_buf[1] ____cacheline_aligned;
+ u8 reg_read_tx_buf[2];
+ u8 fifo_tx_buf[1];
+};
+
+static int adxl367_read_fifo(void *context, __be16 *fifo_buf,
+ unsigned int fifo_entries)
+{
+ struct adxl367_spi_state *st = context;
+
+ st->fifo_xfer[1].rx_buf = fifo_buf;
+ st->fifo_xfer[1].len = fifo_entries * sizeof(*fifo_buf);
+
+ return spi_sync(st->spi, &st->fifo_msg);
+}
+
+static int adxl367_read(void *context, const void *reg_buf, size_t reg_size,
+ void *val_buf, size_t val_size)
+{
+ struct adxl367_spi_state *st = context;
+ u8 reg = ((const u8 *)reg_buf)[0];
+
+ st->reg_read_tx_buf[1] = reg;
+ st->reg_read_xfer[1].rx_buf = val_buf;
+ st->reg_read_xfer[1].len = val_size;
+
+ return spi_sync(st->spi, &st->reg_read_msg);
+}
+
+static int adxl367_write(void *context, const void *val_buf, size_t val_size)
+{
+ struct adxl367_spi_state *st = context;
+
+ st->reg_write_xfer[1].tx_buf = val_buf;
+ st->reg_write_xfer[1].len = val_size;
+
+ return spi_sync(st->spi, &st->reg_write_msg);
+}
+
+static struct regmap_bus adxl367_spi_regmap_bus = {
+ .read = adxl367_read,
+ .write = adxl367_write,
+};
+
+static const struct regmap_config adxl367_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct adxl367_ops adxl367_spi_ops = {
+ .read_fifo = adxl367_read_fifo,
+};
+
+static int adxl367_spi_probe(struct spi_device *spi)
+{
+ struct adxl367_spi_state *st;
+ struct regmap *regmap;
+
+ st = devm_kzalloc(&spi->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->spi = spi;
+
+ /*
+ * Xfer: [XFR1] [ XFR2 ]
+ * Master: 0x0A ADDR DATA0 DATA1 ... DATAN
+ * Slave: .... ..........................
+ */
+ st->reg_write_tx_buf[0] = ADXL367_SPI_WRITE_COMMAND;
+ st->reg_write_xfer[0].tx_buf = st->reg_write_tx_buf;
+ st->reg_write_xfer[0].len = sizeof(st->reg_write_tx_buf);
+ spi_message_init_with_transfers(&st->reg_write_msg,
+ st->reg_write_xfer, 2);
+
+ /*
+ * Xfer: [ XFR1 ] [ XFR2 ]
+ * Master: 0x0B ADDR .....................
+ * Slave: ......... DATA0 DATA1 ... DATAN
+ */
+ st->reg_read_tx_buf[0] = ADXL367_SPI_READ_COMMAND;
+ st->reg_read_xfer[0].tx_buf = st->reg_read_tx_buf;
+ st->reg_read_xfer[0].len = sizeof(st->reg_read_tx_buf);
+ spi_message_init_with_transfers(&st->reg_read_msg,
+ st->reg_read_xfer, 2);
+
+ /*
+ * Xfer: [XFR1] [ XFR2 ]
+ * Master: 0x0D .....................
+ * Slave: .... DATA0 DATA1 ... DATAN
+ */
+ st->fifo_tx_buf[0] = ADXL367_SPI_FIFO_COMMAND;
+ st->fifo_xfer[0].tx_buf = st->fifo_tx_buf;
+ st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf);
+ spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, 2);
+
+ regmap = devm_regmap_init(&spi->dev, &adxl367_spi_regmap_bus, st,
+ &adxl367_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return adxl367_probe(&spi->dev, &adxl367_spi_ops, st, regmap, spi->irq);
+}
+
+static const struct spi_device_id adxl367_spi_id[] = {
+ { "adxl367", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, adxl367_spi_id);
+
+static const struct of_device_id adxl367_of_match[] = {
+ { .compatible = "adi,adxl367" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adxl367_of_match);
+
+static struct spi_driver adxl367_spi_driver = {
+ .driver = {
+ .name = "adxl367_spi",
+ .of_match_table = adxl367_of_match,
+ },
+ .probe = adxl367_spi_probe,
+ .id_table = adxl367_spi_id,
+};
+
+module_spi_driver(adxl367_spi_driver);
+
+MODULE_IMPORT_NS(IIO_ADXL367);
+MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index 758952584f8c..e3ecbaee61f7 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -1176,7 +1176,7 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg)
{
return (reg == ADXL372_FIFO_DATA);
}
-EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg);
+EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, IIO_ADXL372);
int adxl372_probe(struct device *dev, struct regmap *regmap,
int irq, const char *name)
@@ -1260,7 +1260,7 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(adxl372_probe);
+EXPORT_SYMBOL_NS_GPL(adxl372_probe, IIO_ADXL372);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver");
diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c
index 9a07ab3d151a..4efb70a5fe40 100644
--- a/drivers/iio/accel/adxl372_i2c.c
+++ b/drivers/iio/accel/adxl372_i2c.c
@@ -67,3 +67,4 @@ module_i2c_driver(adxl372_i2c_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_ADXL372);
diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c
index 1f1352fee99a..2bd267a22f29 100644
--- a/drivers/iio/accel/adxl372_spi.c
+++ b/drivers/iio/accel/adxl372_spi.c
@@ -59,3 +59,4 @@ module_spi_driver(adxl372_spi_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_ADXL372);
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index d8a454c266d5..4f73bc827eec 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -1065,7 +1065,6 @@ static int bma180_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int bma180_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -1092,11 +1091,7 @@ static int bma180_resume(struct device *dev)
return ret;
}
-static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume);
-#define BMA180_PM_OPS (&bma180_pm_ops)
-#else
-#define BMA180_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume);
static const struct i2c_device_id bma180_ids[] = {
{ "bma023", BMA023 },
@@ -1137,7 +1132,7 @@ MODULE_DEVICE_TABLE(of, bma180_of_match);
static struct i2c_driver bma180_driver = {
.driver = {
.name = "bma180",
- .pm = BMA180_PM_OPS,
+ .pm = pm_sleep_ptr(&bma180_pm_ops),
.of_match_table = bma180_of_match,
},
.probe = bma180_probe,
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index fd2647b728d3..043002fe6f63 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -136,7 +136,7 @@ const struct regmap_config bma400_regmap_config = {
.writeable_reg = bma400_is_writable_reg,
.volatile_reg = bma400_is_volatile_reg,
};
-EXPORT_SYMBOL(bma400_regmap_config);
+EXPORT_SYMBOL_NS(bma400_regmap_config, IIO_BMA400);
static const struct iio_mount_matrix *
bma400_accel_get_mount_matrix(const struct iio_dev *indio_dev,
@@ -826,7 +826,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
return iio_device_register(indio_dev);
}
-EXPORT_SYMBOL(bma400_probe);
+EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);
void bma400_remove(struct device *dev)
{
@@ -846,7 +846,7 @@ void bma400_remove(struct device *dev)
iio_device_unregister(indio_dev);
}
-EXPORT_SYMBOL(bma400_remove);
+EXPORT_SYMBOL_NS(bma400_remove, IIO_BMA400);
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core");
diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c
index f50df5310beb..da104ffd3fe0 100644
--- a/drivers/iio/accel/bma400_i2c.c
+++ b/drivers/iio/accel/bma400_i2c.c
@@ -61,3 +61,4 @@ module_i2c_driver(bma400_i2c_driver);
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (I2C)");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BMA400);
diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c
index 9040a717b247..51f23bdc0ea5 100644
--- a/drivers/iio/accel/bma400_spi.c
+++ b/drivers/iio/accel/bma400_spi.c
@@ -118,3 +118,4 @@ module_spi_driver(bma400_spi_driver);
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (SPI)");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BMA400);
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index d11f668016a6..7516d7dde1af 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -203,7 +203,7 @@ const struct regmap_config bmc150_regmap_conf = {
.val_bits = 8,
.max_register = 0x3f,
};
-EXPORT_SYMBOL_GPL(bmc150_regmap_conf);
+EXPORT_SYMBOL_NS_GPL(bmc150_regmap_conf, IIO_BMC150);
static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
enum bmc150_power_modes mode,
@@ -1801,7 +1801,7 @@ err_disable_regulators:
return ret;
}
-EXPORT_SYMBOL_GPL(bmc150_accel_core_probe);
+EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_probe, IIO_BMC150);
void bmc150_accel_core_remove(struct device *dev)
{
@@ -1824,7 +1824,7 @@ void bmc150_accel_core_remove(struct device *dev)
regulator_bulk_disable(ARRAY_SIZE(data->regulators),
data->regulators);
}
-EXPORT_SYMBOL_GPL(bmc150_accel_core_remove);
+EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_remove, IIO_BMC150);
#ifdef CONFIG_PM_SLEEP
static int bmc150_accel_suspend(struct device *dev)
@@ -1899,7 +1899,7 @@ const struct dev_pm_ops bmc150_accel_pm_ops = {
SET_RUNTIME_PM_OPS(bmc150_accel_runtime_suspend,
bmc150_accel_runtime_resume, NULL)
};
-EXPORT_SYMBOL_GPL(bmc150_accel_pm_ops);
+EXPORT_SYMBOL_NS_GPL(bmc150_accel_pm_ops, IIO_BMC150);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c
index 9e52df9a8f07..dff4d7dd101c 100644
--- a/drivers/iio/accel/bmc150-accel-i2c.c
+++ b/drivers/iio/accel/bmc150-accel-i2c.c
@@ -280,3 +280,4 @@ module_i2c_driver(bmc150_accel_driver);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMC150 I2C accelerometer driver");
+MODULE_IMPORT_NS(IIO_BMC150);
diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c
index 80007cc2d044..921fb46be0b8 100644
--- a/drivers/iio/accel/bmc150-accel-spi.c
+++ b/drivers/iio/accel/bmc150-accel-spi.c
@@ -82,3 +82,4 @@ module_spi_driver(bmc150_accel_driver);
MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMC150 SPI accelerometer driver");
+MODULE_IMPORT_NS(IIO_BMC150);
diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c
index d74465214feb..8b2728bbcade 100644
--- a/drivers/iio/accel/bmi088-accel-core.c
+++ b/drivers/iio/accel/bmi088-accel-core.c
@@ -146,7 +146,7 @@ const struct regmap_config bmi088_regmap_conf = {
.volatile_table = &bmi088_volatile_table,
.cache_type = REGCACHE_RBTREE,
};
-EXPORT_SYMBOL_GPL(bmi088_regmap_conf);
+EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088);
static int bmi088_accel_power_up(struct bmi088_accel_data *data)
{
@@ -533,7 +533,7 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
return ret;
}
-EXPORT_SYMBOL_GPL(bmi088_accel_core_probe);
+EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088);
void bmi088_accel_core_remove(struct device *dev)
@@ -547,7 +547,7 @@ void bmi088_accel_core_remove(struct device *dev)
pm_runtime_set_suspended(dev);
bmi088_accel_power_down(data);
}
-EXPORT_SYMBOL_GPL(bmi088_accel_core_remove);
+EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088);
static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev)
{
@@ -571,7 +571,7 @@ const struct dev_pm_ops bmi088_accel_pm_ops = {
SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend,
bmi088_accel_runtime_resume, NULL)
};
-EXPORT_SYMBOL_GPL(bmi088_accel_pm_ops);
+EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088);
MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c
index 06d99d9949f3..167c36cf1eb8 100644
--- a/drivers/iio/accel/bmi088-accel-spi.c
+++ b/drivers/iio/accel/bmi088-accel-spi.c
@@ -81,3 +81,4 @@ module_spi_driver(bmi088_accel_driver);
MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMI088 accelerometer driver (SPI)");
+MODULE_IMPORT_NS(IIO_BMI088);
diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c
index 9633bdae5fd4..04e9c5678964 100644
--- a/drivers/iio/accel/da280.c
+++ b/drivers/iio/accel/da280.c
@@ -153,7 +153,6 @@ static int da280_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int da280_suspend(struct device *dev)
{
return da280_enable(to_i2c_client(dev), false);
@@ -163,9 +162,8 @@ static int da280_resume(struct device *dev)
{
return da280_enable(to_i2c_client(dev), true);
}
-#endif
-static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
static const struct acpi_device_id da280_acpi_match[] = {
{"MIRAACC", da280},
@@ -184,7 +182,7 @@ static struct i2c_driver da280_driver = {
.driver = {
.name = "da280",
.acpi_match_table = ACPI_PTR(da280_acpi_match),
- .pm = &da280_pm_ops,
+ .pm = pm_sleep_ptr(&da280_pm_ops),
},
.probe = da280_probe,
.id_table = da280_i2c_id,
diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c
index 04e13487e706..ec4e29d260f7 100644
--- a/drivers/iio/accel/da311.c
+++ b/drivers/iio/accel/da311.c
@@ -256,7 +256,6 @@ static int da311_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int da311_suspend(struct device *dev)
{
return da311_enable(to_i2c_client(dev), false);
@@ -266,9 +265,8 @@ static int da311_resume(struct device *dev)
{
return da311_enable(to_i2c_client(dev), true);
}
-#endif
-static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
static const struct i2c_device_id da311_i2c_id[] = {
{"da311", 0},
@@ -279,7 +277,7 @@ MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
static struct i2c_driver da311_driver = {
.driver = {
.name = "da311",
- .pm = &da311_pm_ops,
+ .pm = pm_sleep_ptr(&da311_pm_ops),
},
.probe = da311_probe,
.id_table = da311_i2c_id,
diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c
index de2868c28d95..4b69c8530f5e 100644
--- a/drivers/iio/accel/dmard06.c
+++ b/drivers/iio/accel/dmard06.c
@@ -170,7 +170,6 @@ static int dmard06_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int dmard06_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -199,11 +198,8 @@ static int dmard06_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume);
-#define DMARD06_PM_OPS (&dmard06_pm_ops)
-#else
-#define DMARD06_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend,
+ dmard06_resume);
static const struct i2c_device_id dmard06_id[] = {
{ "dmard05", 0 },
@@ -227,7 +223,7 @@ static struct i2c_driver dmard06_driver = {
.driver = {
.name = DMARD06_DRV_NAME,
.of_match_table = dmard06_of_match,
- .pm = DMARD06_PM_OPS,
+ .pm = pm_sleep_ptr(&dmard06_pm_ops),
},
};
module_i2c_driver(dmard06_driver);
diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c
index e6e28c964777..53ab6078cb7f 100644
--- a/drivers/iio/accel/dmard09.c
+++ b/drivers/iio/accel/dmard09.c
@@ -126,7 +126,7 @@ static int dmard09_probe(struct i2c_client *client,
}
static const struct i2c_device_id dmard09_id[] = {
- { "dmard09", 0},
+ { "dmard09", 0 },
{ },
};
diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c
index f9f173eec202..8ac62ec0a04a 100644
--- a/drivers/iio/accel/dmard10.c
+++ b/drivers/iio/accel/dmard10.c
@@ -218,7 +218,6 @@ static int dmard10_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int dmard10_suspend(struct device *dev)
{
return dmard10_shutdown(to_i2c_client(dev));
@@ -228,9 +227,9 @@ static int dmard10_resume(struct device *dev)
{
return dmard10_reset(to_i2c_client(dev));
}
-#endif
-static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend,
+ dmard10_resume);
static const struct i2c_device_id dmard10_i2c_id[] = {
{"dmard10", 0},
@@ -241,7 +240,7 @@ MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
static struct i2c_driver dmard10_driver = {
.driver = {
.name = "dmard10",
- .pm = &dmard10_pm_ops,
+ .pm = pm_sleep_ptr(&dmard10_pm_ops),
},
.probe = dmard10_probe,
.id_table = dmard10_i2c_id,
diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c
index f7fd9e046588..a9d2f10d5d45 100644
--- a/drivers/iio/accel/fxls8962af-core.c
+++ b/drivers/iio/accel/fxls8962af-core.c
@@ -178,7 +178,7 @@ const struct regmap_config fxls8962af_i2c_regmap_conf = {
.val_bits = 8,
.max_register = FXLS8962AF_MAX_REG,
};
-EXPORT_SYMBOL_GPL(fxls8962af_i2c_regmap_conf);
+EXPORT_SYMBOL_NS_GPL(fxls8962af_i2c_regmap_conf, IIO_FXLS8962AF);
const struct regmap_config fxls8962af_spi_regmap_conf = {
.reg_bits = 8,
@@ -186,7 +186,7 @@ const struct regmap_config fxls8962af_spi_regmap_conf = {
.val_bits = 8,
.max_register = FXLS8962AF_MAX_REG,
};
-EXPORT_SYMBOL_GPL(fxls8962af_spi_regmap_conf);
+EXPORT_SYMBOL_NS_GPL(fxls8962af_spi_regmap_conf, IIO_FXLS8962AF);
enum {
fxls8962af_idx_x,
@@ -1240,7 +1240,7 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq)
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(fxls8962af_core_probe);
+EXPORT_SYMBOL_NS_GPL(fxls8962af_core_probe, IIO_FXLS8962AF);
static int __maybe_unused fxls8962af_runtime_suspend(struct device *dev)
{
@@ -1306,7 +1306,7 @@ const struct dev_pm_ops fxls8962af_pm_ops = {
SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend,
fxls8962af_runtime_resume, NULL)
};
-EXPORT_SYMBOL_GPL(fxls8962af_pm_ops);
+EXPORT_SYMBOL_NS_GPL(fxls8962af_pm_ops, IIO_FXLS8962AF);
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>");
MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer driver");
diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c
index 6bde9891effb..8fbadfea1620 100644
--- a/drivers/iio/accel/fxls8962af-i2c.c
+++ b/drivers/iio/accel/fxls8962af-i2c.c
@@ -55,3 +55,4 @@ module_i2c_driver(fxls8962af_driver);
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>");
MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_FXLS8962AF);
diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c
index 6f4dff3238d3..885b3ab7fcb5 100644
--- a/drivers/iio/accel/fxls8962af-spi.c
+++ b/drivers/iio/accel/fxls8962af-spi.c
@@ -55,3 +55,4 @@ module_spi_driver(fxls8962af_driver);
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>");
MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_FXLS8962AF);
diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c
index 274b41a6e603..c8dc52f11037 100644
--- a/drivers/iio/accel/kxsd9-i2c.c
+++ b/drivers/iio/accel/kxsd9-i2c.c
@@ -65,3 +65,4 @@ module_i2c_driver(kxsd9_i2c_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("KXSD9 accelerometer I2C interface");
+MODULE_IMPORT_NS(IIO_KXSD9);
diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c
index 57c451cfb9e5..ec17e35e573e 100644
--- a/drivers/iio/accel/kxsd9-spi.c
+++ b/drivers/iio/accel/kxsd9-spi.c
@@ -64,3 +64,4 @@ module_spi_driver(kxsd9_spi_driver);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_KXSD9);
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 552eba5e8b4f..3975860331a6 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -476,7 +476,7 @@ err_power_down:
return ret;
}
-EXPORT_SYMBOL(kxsd9_common_probe);
+EXPORT_SYMBOL_NS(kxsd9_common_probe, IIO_KXSD9);
void kxsd9_common_remove(struct device *dev)
{
@@ -490,7 +490,7 @@ void kxsd9_common_remove(struct device *dev)
pm_runtime_disable(dev);
kxsd9_power_down(st);
}
-EXPORT_SYMBOL(kxsd9_common_remove);
+EXPORT_SYMBOL_NS(kxsd9_common_remove, IIO_KXSD9);
#ifdef CONFIG_PM
static int kxsd9_runtime_suspend(struct device *dev)
@@ -516,7 +516,7 @@ const struct dev_pm_ops kxsd9_dev_pm_ops = {
SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend,
kxsd9_runtime_resume, NULL)
};
-EXPORT_SYMBOL(kxsd9_dev_pm_ops);
+EXPORT_SYMBOL_NS(kxsd9_dev_pm_ops, IIO_KXSD9);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Kionix KXSD9 driver");
diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c
index 735002b716f3..679e69cd7657 100644
--- a/drivers/iio/accel/mc3230.c
+++ b/drivers/iio/accel/mc3230.c
@@ -160,7 +160,6 @@ static int mc3230_remove(struct i2c_client *client)
return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
}
-#ifdef CONFIG_PM_SLEEP
static int mc3230_suspend(struct device *dev)
{
struct mc3230_data *data;
@@ -178,9 +177,8 @@ static int mc3230_resume(struct device *dev)
return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
}
-#endif
-static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
static const struct i2c_device_id mc3230_i2c_id[] = {
{"mc3230", 0},
@@ -191,7 +189,7 @@ MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
static struct i2c_driver mc3230_driver = {
.driver = {
.name = "mc3230",
- .pm = &mc3230_pm_ops,
+ .pm = pm_sleep_ptr(&mc3230_pm_ops),
},
.probe = mc3230_probe,
.remove = mc3230_remove,
diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c
index e6739ba74edf..a34195b3215d 100644
--- a/drivers/iio/accel/mma7455_core.c
+++ b/drivers/iio/accel/mma7455_core.c
@@ -238,7 +238,7 @@ const struct regmap_config mma7455_core_regmap = {
.val_bits = 8,
.max_register = MMA7455_REG_TW,
};
-EXPORT_SYMBOL_GPL(mma7455_core_regmap);
+EXPORT_SYMBOL_NS_GPL(mma7455_core_regmap, IIO_MMA7455);
int mma7455_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
@@ -293,7 +293,7 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap,
return 0;
}
-EXPORT_SYMBOL_GPL(mma7455_core_probe);
+EXPORT_SYMBOL_NS_GPL(mma7455_core_probe, IIO_MMA7455);
void mma7455_core_remove(struct device *dev)
{
@@ -306,7 +306,7 @@ void mma7455_core_remove(struct device *dev)
regmap_write(mma7455->regmap, MMA7455_REG_MCTL,
MMA7455_MCTL_MODE_STANDBY);
}
-EXPORT_SYMBOL_GPL(mma7455_core_remove);
+EXPORT_SYMBOL_NS_GPL(mma7455_core_remove, IIO_MMA7455);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver");
diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c
index 8a5256516f9f..a3b84e8a3ea8 100644
--- a/drivers/iio/accel/mma7455_i2c.c
+++ b/drivers/iio/accel/mma7455_i2c.c
@@ -61,3 +61,4 @@ module_i2c_driver(mma7455_i2c_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MMA7455);
diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c
index b746031551a3..fcdde2e8a84b 100644
--- a/drivers/iio/accel/mma7455_spi.c
+++ b/drivers/iio/accel/mma7455_spi.c
@@ -47,3 +47,4 @@ module_spi_driver(mma7455_spi_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MMA7455);
diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c
index 24b83ccdb950..112a5a33c29f 100644
--- a/drivers/iio/accel/mma7660.c
+++ b/drivers/iio/accel/mma7660.c
@@ -222,7 +222,6 @@ static int mma7660_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int mma7660_suspend(struct device *dev)
{
struct mma7660_data *data;
@@ -241,12 +240,8 @@ static int mma7660_resume(struct device *dev)
return mma7660_set_mode(data, MMA7660_MODE_ACTIVE);
}
-static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume);
-
-#define MMA7660_PM_OPS (&mma7660_pm_ops)
-#else
-#define MMA7660_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend,
+ mma7660_resume);
static const struct i2c_device_id mma7660_i2c_id[] = {
{"mma7660", 0},
@@ -270,7 +265,7 @@ MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id);
static struct i2c_driver mma7660_driver = {
.driver = {
.name = "mma7660",
- .pm = MMA7660_PM_OPS,
+ .pm = pm_sleep_ptr(&mma7660_pm_ops),
.of_match_table = mma7660_of_match,
.acpi_match_table = ACPI_PTR(mma7660_acpi_id),
},
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 64b82b4503ad..9c02c681c84c 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -104,6 +104,7 @@
struct mma8452_data {
struct i2c_client *client;
struct mutex lock;
+ struct iio_mount_matrix orientation;
u8 ctrl_reg1;
u8 data_cfg;
const struct mma_chip_info *chip_info;
@@ -176,6 +177,7 @@ static const struct mma8452_event_regs trans_ev_regs = {
* @enabled_events: event flags enabled and handled by this driver
*/
struct mma_chip_info {
+ const char *name;
u8 chip_id;
const struct iio_chan_spec *channels;
int num_channels;
@@ -379,8 +381,8 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct mma8452_data *data = iio_priv(i2c_get_clientdata(
- to_i2c_client(dev)));
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct mma8452_data *data = iio_priv(indio_dev);
return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
ARRAY_SIZE(data->chip_info->mma_scales));
@@ -1189,6 +1191,20 @@ static const struct attribute_group mma8452_event_attribute_group = {
.attrs = mma8452_event_attributes,
};
+static const struct iio_mount_matrix *
+mma8452_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info mma8452_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mma8452_get_mount_matrix),
+ { }
+};
+
#define MMA8452_FREEFALL_CHANNEL(modifier) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -1227,6 +1243,7 @@ static const struct attribute_group mma8452_event_attribute_group = {
}, \
.event_spec = mma8452_transient_event, \
.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
+ .ext_info = mma8452_ext_info, \
}
#define MMA8652_CHANNEL(axis, idx, bits) { \
@@ -1248,6 +1265,7 @@ static const struct attribute_group mma8452_event_attribute_group = {
}, \
.event_spec = mma8452_motion_event, \
.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
+ .ext_info = mma8452_ext_info, \
}
static const struct iio_chan_spec mma8451_channels[] = {
@@ -1301,6 +1319,7 @@ enum {
static const struct mma_chip_info mma_chip_info_table[] = {
[mma8451] = {
+ .name = "mma8451",
.chip_id = MMA8451_DEVICE_ID,
.channels = mma8451_channels,
.num_channels = ARRAY_SIZE(mma8451_channels),
@@ -1325,6 +1344,7 @@ static const struct mma_chip_info mma_chip_info_table[] = {
MMA8452_INT_FF_MT,
},
[mma8452] = {
+ .name = "mma8452",
.chip_id = MMA8452_DEVICE_ID,
.channels = mma8452_channels,
.num_channels = ARRAY_SIZE(mma8452_channels),
@@ -1341,6 +1361,7 @@ static const struct mma_chip_info mma_chip_info_table[] = {
MMA8452_INT_FF_MT,
},
[mma8453] = {
+ .name = "mma8453",
.chip_id = MMA8453_DEVICE_ID,
.channels = mma8453_channels,
.num_channels = ARRAY_SIZE(mma8453_channels),
@@ -1357,6 +1378,7 @@ static const struct mma_chip_info mma_chip_info_table[] = {
MMA8452_INT_FF_MT,
},
[mma8652] = {
+ .name = "mma8652",
.chip_id = MMA8652_DEVICE_ID,
.channels = mma8652_channels,
.num_channels = ARRAY_SIZE(mma8652_channels),
@@ -1366,6 +1388,7 @@ static const struct mma_chip_info mma_chip_info_table[] = {
.enabled_events = MMA8452_INT_FF_MT,
},
[mma8653] = {
+ .name = "mma8653",
.chip_id = MMA8653_DEVICE_ID,
.channels = mma8653_channels,
.num_channels = ARRAY_SIZE(mma8653_channels),
@@ -1380,6 +1403,7 @@ static const struct mma_chip_info mma_chip_info_table[] = {
.enabled_events = MMA8452_INT_FF_MT,
},
[fxls8471] = {
+ .name = "fxls8471",
.chip_id = FXLS8471_DEVICE_ID,
.channels = mma8451_channels,
.num_channels = ARRAY_SIZE(mma8451_channels),
@@ -1522,13 +1546,6 @@ static int mma8452_probe(struct i2c_client *client,
struct mma8452_data *data;
struct iio_dev *indio_dev;
int ret;
- const struct of_device_id *match;
-
- match = of_match_device(mma8452_dt_ids, &client->dev);
- if (!match) {
- dev_err(&client->dev, "unknown device model\n");
- return -ENODEV;
- }
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -1537,7 +1554,18 @@ static int mma8452_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
- data->chip_info = match->data;
+
+ data->chip_info = device_get_match_data(&client->dev);
+ if (!data->chip_info && id) {
+ data->chip_info = &mma_chip_info_table[id->driver_data];
+ } else {
+ dev_err(&client->dev, "unknown device model\n");
+ return -ENODEV;
+ }
+
+ ret = iio_read_mount_matrix(&client->dev, &data->orientation);
+ if (ret)
+ return ret;
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg))
@@ -1581,11 +1609,11 @@ static int mma8452_probe(struct i2c_client *client,
}
dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
- match->compatible, data->chip_info->chip_id);
+ data->chip_info->name, data->chip_info->chip_id);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mma8452_info;
- indio_dev->name = id->name;
+ indio_dev->name = data->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->num_channels;
@@ -1810,7 +1838,7 @@ MODULE_DEVICE_TABLE(i2c, mma8452_id);
static struct i2c_driver mma8452_driver = {
.driver = {
.name = "mma8452",
- .of_match_table = of_match_ptr(mma8452_dt_ids),
+ .of_match_table = mma8452_dt_ids,
.pm = &mma8452_pm_ops,
},
.probe = mma8452_probe,
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index c53a3398b14c..123cdbbb265c 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -526,7 +526,6 @@ static int mma9551_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
static int mma9551_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -558,9 +557,7 @@ static int mma9551_runtime_resume(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int mma9551_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -586,12 +583,10 @@ static int mma9551_resume(struct device *dev)
return ret;
}
-#endif
static const struct dev_pm_ops mma9551_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
- SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
- mma9551_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
+ RUNTIME_PM_OPS(mma9551_runtime_suspend, mma9551_runtime_resume, NULL)
};
static const struct acpi_device_id mma9551_acpi_match[] = {
@@ -612,7 +607,7 @@ static struct i2c_driver mma9551_driver = {
.driver = {
.name = MMA9551_DRV_NAME,
.acpi_match_table = ACPI_PTR(mma9551_acpi_match),
- .pm = &mma9551_pm_ops,
+ .pm = pm_ptr(&mma9551_pm_ops),
},
.probe = mma9551_probe,
.remove = mma9551_remove,
@@ -625,3 +620,4 @@ MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver");
+MODULE_IMPORT_NS(IIO_MMA9551);
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
index fbf2e2c45678..64ca7d7a9673 100644
--- a/drivers/iio/accel/mma9551_core.c
+++ b/drivers/iio/accel/mma9551_core.c
@@ -219,7 +219,7 @@ int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, val, 1);
}
-EXPORT_SYMBOL(mma9551_read_config_byte);
+EXPORT_SYMBOL_NS(mma9551_read_config_byte, IIO_MMA9551);
/**
* mma9551_write_config_byte() - write 1 configuration byte
@@ -244,7 +244,7 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
&val, 1, NULL, 0);
}
-EXPORT_SYMBOL(mma9551_write_config_byte);
+EXPORT_SYMBOL_NS(mma9551_write_config_byte, IIO_MMA9551);
/**
* mma9551_read_status_byte() - read 1 status byte
@@ -269,7 +269,7 @@ int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, val, 1);
}
-EXPORT_SYMBOL(mma9551_read_status_byte);
+EXPORT_SYMBOL_NS(mma9551_read_status_byte, IIO_MMA9551);
/**
* mma9551_read_config_word() - read 1 config word
@@ -300,7 +300,7 @@ int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
return ret;
}
-EXPORT_SYMBOL(mma9551_read_config_word);
+EXPORT_SYMBOL_NS(mma9551_read_config_word, IIO_MMA9551);
/**
* mma9551_write_config_word() - write 1 config word
@@ -327,7 +327,7 @@ int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
(u8 *)&v, 2, NULL, 0);
}
-EXPORT_SYMBOL(mma9551_write_config_word);
+EXPORT_SYMBOL_NS(mma9551_write_config_word, IIO_MMA9551);
/**
* mma9551_read_status_word() - read 1 status word
@@ -358,7 +358,7 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
return ret;
}
-EXPORT_SYMBOL(mma9551_read_status_word);
+EXPORT_SYMBOL_NS(mma9551_read_status_word, IIO_MMA9551);
/**
* mma9551_read_config_words() - read multiple config words
@@ -397,7 +397,7 @@ int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
return 0;
}
-EXPORT_SYMBOL(mma9551_read_config_words);
+EXPORT_SYMBOL_NS(mma9551_read_config_words, IIO_MMA9551);
/**
* mma9551_read_status_words() - read multiple status words
@@ -436,7 +436,7 @@ int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
return 0;
}
-EXPORT_SYMBOL(mma9551_read_status_words);
+EXPORT_SYMBOL_NS(mma9551_read_status_words, IIO_MMA9551);
/**
* mma9551_write_config_words() - write multiple config words
@@ -471,7 +471,7 @@ int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
reg, (u8 *)be_buf, len * sizeof(u16), NULL, 0);
}
-EXPORT_SYMBOL(mma9551_write_config_words);
+EXPORT_SYMBOL_NS(mma9551_write_config_words, IIO_MMA9551);
/**
* mma9551_update_config_bits() - update bits in register
@@ -507,7 +507,7 @@ int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
return mma9551_write_config_byte(client, app_id, reg, tmp);
}
-EXPORT_SYMBOL(mma9551_update_config_bits);
+EXPORT_SYMBOL_NS(mma9551_update_config_bits, IIO_MMA9551);
/**
* mma9551_gpio_config() - configure gpio
@@ -586,7 +586,7 @@ int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
return ret;
}
-EXPORT_SYMBOL(mma9551_gpio_config);
+EXPORT_SYMBOL_NS(mma9551_gpio_config, IIO_MMA9551);
/**
* mma9551_read_version() - read device version information
@@ -616,7 +616,7 @@ int mma9551_read_version(struct i2c_client *client)
return 0;
}
-EXPORT_SYMBOL(mma9551_read_version);
+EXPORT_SYMBOL_NS(mma9551_read_version, IIO_MMA9551);
/**
* mma9551_set_device_state() - sets HW power mode
@@ -646,7 +646,7 @@ int mma9551_set_device_state(struct i2c_client *client, bool enable)
MMA9551_SLEEP_CFG_FLEEN :
MMA9551_SLEEP_CFG_SNCEN);
}
-EXPORT_SYMBOL(mma9551_set_device_state);
+EXPORT_SYMBOL_NS(mma9551_set_device_state, IIO_MMA9551);
/**
* mma9551_set_power_state() - sets runtime PM state
@@ -680,7 +680,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on)
return 0;
}
-EXPORT_SYMBOL(mma9551_set_power_state);
+EXPORT_SYMBOL_NS(mma9551_set_power_state, IIO_MMA9551);
/**
* mma9551_sleep() - sleep
@@ -699,7 +699,7 @@ void mma9551_sleep(int freq)
else
msleep_interruptible(sleep_val);
}
-EXPORT_SYMBOL(mma9551_sleep);
+EXPORT_SYMBOL_NS(mma9551_sleep, IIO_MMA9551);
/**
* mma9551_read_accel_chan() - read accelerometer channel
@@ -755,7 +755,7 @@ out_poweroff:
mma9551_set_power_state(client, false);
return ret;
}
-EXPORT_SYMBOL(mma9551_read_accel_chan);
+EXPORT_SYMBOL_NS(mma9551_read_accel_chan, IIO_MMA9551);
/**
* mma9551_read_accel_scale() - read accelerometer scale
@@ -773,7 +773,7 @@ int mma9551_read_accel_scale(int *val, int *val2)
return IIO_VAL_INT_PLUS_MICRO;
}
-EXPORT_SYMBOL(mma9551_read_accel_scale);
+EXPORT_SYMBOL_NS(mma9551_read_accel_scale, IIO_MMA9551);
/**
* mma9551_app_reset() - reset application
@@ -792,7 +792,7 @@ int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
MMA9551_RSC_OFFSET(app_mask),
MMA9551_RSC_VAL(app_mask));
}
-EXPORT_SYMBOL(mma9551_app_reset);
+EXPORT_SYMBOL_NS(mma9551_app_reset, IIO_MMA9551);
MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index 5ff6bc70708b..09df58d4be33 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -1165,7 +1165,6 @@ static int mma9553_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
static int mma9553_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -1197,9 +1196,7 @@ static int mma9553_runtime_resume(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int mma9553_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -1225,12 +1222,10 @@ static int mma9553_resume(struct device *dev)
return ret;
}
-#endif
static const struct dev_pm_ops mma9553_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
- SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
- mma9553_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
+ RUNTIME_PM_OPS(mma9553_runtime_suspend, mma9553_runtime_resume, NULL)
};
static const struct acpi_device_id mma9553_acpi_match[] = {
@@ -1251,7 +1246,7 @@ static struct i2c_driver mma9553_driver = {
.driver = {
.name = MMA9553_DRV_NAME,
.acpi_match_table = ACPI_PTR(mma9553_acpi_match),
- .pm = &mma9553_pm_ops,
+ .pm = pm_ptr(&mma9553_pm_ops),
},
.probe = mma9553_probe,
.remove = mma9553_remove,
@@ -1263,3 +1258,4 @@ module_i2c_driver(mma9553_driver);
MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
+MODULE_IMPORT_NS(IIO_MMA9551);
diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
index 04dcb2b657ee..a1164b439f41 100644
--- a/drivers/iio/accel/ssp_accel_sensor.c
+++ b/drivers/iio/accel/ssp_accel_sensor.c
@@ -142,3 +142,4 @@ module_platform_driver(ssp_accel_driver);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_SSP_SENSORS);
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index 8750dea56fcb..00e056c21bfc 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -36,6 +36,7 @@ enum st_accel_type {
LIS3DHH,
LIS2DE12,
LIS2HH12,
+ SC7A20,
ST_ACCEL_MAX,
};
@@ -61,6 +62,7 @@ enum st_accel_type {
#define LIS3DE_ACCEL_DEV_NAME "lis3de"
#define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
#define LIS2HH12_ACCEL_DEV_NAME "lis2hh12"
+#define SC7A20_ACCEL_DEV_NAME "sc7a20"
#ifdef CONFIG_IIO_BUFFER
int st_accel_allocate_ring(struct iio_dev *indio_dev);
diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c
index fc82fa83f1fb..b2977ae19b69 100644
--- a/drivers/iio/accel/st_accel_buffer.c
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -7,7 +7,6 @@
* Denis Ciocca <denis.ciocca@st.com>
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@@ -65,7 +64,3 @@ int st_accel_allocate_ring(struct iio_dev *indio_dev)
return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
NULL, &st_sensors_trigger_handler, &st_accel_buffer_setup_ops);
}
-
-MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 31ea19d0ba71..5c5da6fdb490 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -1087,6 +1087,89 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.multi_read_bit = true,
.bootime = 2,
},
+ {
+ /*
+ * Not an ST part. Register-compatible with the LIS2DH, even
+ * though the WAI value is different.
+ */
+ .wai = 0x11,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = SC7A20_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+ .odr = {
+ .addr = 0x20,
+ .mask = 0xf0,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01, },
+ { .hz = 10, .value = 0x02, },
+ { .hz = 25, .value = 0x03, },
+ { .hz = 50, .value = 0x04, },
+ { .hz = 100, .value = 0x05, },
+ { .hz = 200, .value = 0x06, },
+ { .hz = 400, .value = 0x07, },
+ { .hz = 1600, .value = 0x08, },
+ },
+ },
+ .pw = {
+ .addr = 0x20,
+ .mask = 0xf0,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis = {
+ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+ .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+ },
+ .fs = {
+ .addr = 0x23,
+ .mask = 0x30,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_2G,
+ .value = 0x00,
+ .gain = IIO_G_TO_M_S_2(1000),
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_4G,
+ .value = 0x01,
+ .gain = IIO_G_TO_M_S_2(2000),
+ },
+ [2] = {
+ .num = ST_ACCEL_FS_AVL_8G,
+ .value = 0x02,
+ .gain = IIO_G_TO_M_S_2(4000),
+ },
+ [3] = {
+ .num = ST_ACCEL_FS_AVL_16G,
+ .value = 0x03,
+ .gain = IIO_G_TO_M_S_2(12000),
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x23,
+ .mask = 0x80,
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x22,
+ .mask = 0x10,
+ },
+ .addr_ihl = 0x25,
+ .mask_ihl = 0x02,
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x07,
+ },
+ },
+ .sim = {
+ .addr = 0x23,
+ .value = BIT(0),
+ },
+ .multi_read_bit = true,
+ .bootime = 2,
+ },
};
/* Default accel DRDY is available on INT1 pin */
@@ -1329,7 +1412,7 @@ const struct st_sensor_settings *st_accel_get_settings(const char *name)
return &st_accel_sensors_settings[index];
}
-EXPORT_SYMBOL(st_accel_get_settings);
+EXPORT_SYMBOL_NS(st_accel_get_settings, IIO_ST_SENSORS);
int st_accel_common_probe(struct iio_dev *indio_dev)
{
@@ -1383,8 +1466,9 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
return devm_iio_device_register(parent, indio_dev);
}
-EXPORT_SYMBOL(st_accel_common_probe);
+EXPORT_SYMBOL_NS(st_accel_common_probe, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index c0ce78eebad9..96adc4344f4a 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -107,6 +107,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis2hh12",
.data = LIS2HH12_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "silan,sc7a20",
+ .data = SC7A20_ACCEL_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -142,6 +146,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LIS3DE_ACCEL_DEV_NAME },
{ LIS2DE12_ACCEL_DEV_NAME },
{ LIS2HH12_ACCEL_DEV_NAME },
+ { SC7A20_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
@@ -194,3 +199,4 @@ module_i2c_driver(st_accel_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index b74a1c6d03de..108b63d0146c 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -164,3 +164,4 @@ module_spi_driver(st_accel_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
index de0cdf8c1f94..a71dfff3ca4a 100644
--- a/drivers/iio/accel/stk8312.c
+++ b/drivers/iio/accel/stk8312.c
@@ -611,7 +611,6 @@ static int stk8312_remove(struct i2c_client *client)
return stk8312_set_mode(data, STK8312_MODE_STANDBY);
}
-#ifdef CONFIG_PM_SLEEP
static int stk8312_suspend(struct device *dev)
{
struct stk8312_data *data;
@@ -630,12 +629,8 @@ static int stk8312_resume(struct device *dev)
return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
}
-static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
-
-#define STK8312_PM_OPS (&stk8312_pm_ops)
-#else
-#define STK8312_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,
+ stk8312_resume);
static const struct i2c_device_id stk8312_i2c_id[] = {
/* Deprecated in favour of lowercase form */
@@ -648,7 +643,7 @@ MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id);
static struct i2c_driver stk8312_driver = {
.driver = {
.name = STK8312_DRIVER_NAME,
- .pm = STK8312_PM_OPS,
+ .pm = pm_sleep_ptr(&stk8312_pm_ops),
},
.probe = stk8312_probe,
.remove = stk8312_remove,
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
index 517c57ed9e94..0067ec5cbae8 100644
--- a/drivers/iio/accel/stk8ba50.c
+++ b/drivers/iio/accel/stk8ba50.c
@@ -504,7 +504,6 @@ static int stk8ba50_remove(struct i2c_client *client)
return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
}
-#ifdef CONFIG_PM_SLEEP
static int stk8ba50_suspend(struct device *dev)
{
struct stk8ba50_data *data;
@@ -523,12 +522,8 @@ static int stk8ba50_resume(struct device *dev)
return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
}
-static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume);
-
-#define STK8BA50_PM_OPS (&stk8ba50_pm_ops)
-#else
-#define STK8BA50_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend,
+ stk8ba50_resume);
static const struct i2c_device_id stk8ba50_i2c_id[] = {
{"stk8ba50", 0},
@@ -546,7 +541,7 @@ MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id);
static struct i2c_driver stk8ba50_driver = {
.driver = {
.name = "stk8ba50",
- .pm = STK8BA50_PM_OPS,
+ .pm = pm_sleep_ptr(&stk8ba50_pm_ops),
.acpi_match_table = ACPI_PTR(stk8ba50_acpi_id),
},
.probe = stk8ba50_probe,
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4fdc8bfbb407..71ab0a06aa82 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -64,6 +64,17 @@ config AD7266
To compile this driver as a module, choose M here: the module will be
called ad7266.
+config AD7280
+ tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
+ depends on SPI
+ select CRC8
+ help
+ Say yes here to build support for Analog Devices AD7280A
+ Lithium Ion Battery Monitoring System.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7280a
+
config AD7291
tristate "Analog Devices AD7291 ADC driver"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 4a8f1833993b..39d806f6d457 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o
obj-$(CONFIG_AD7124) += ad7124.o
obj-$(CONFIG_AD7192) += ad7192.o
obj-$(CONFIG_AD7266) += ad7266.o
+obj-$(CONFIG_AD7280) += ad7280a.o
obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7292) += ad7292.o
obj-$(CONFIG_AD7298) += ad7298.o
diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c
index 4c46a201d4ef..930ce96e6ff5 100644
--- a/drivers/iio/adc/ab8500-gpadc.c
+++ b/drivers/iio/adc/ab8500-gpadc.c
@@ -942,7 +942,6 @@ static const struct iio_info ab8500_gpadc_info = {
.read_raw = ab8500_gpadc_read_raw,
};
-#ifdef CONFIG_PM
static int ab8500_gpadc_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -965,7 +964,6 @@ static int ab8500_gpadc_runtime_resume(struct device *dev)
return ret;
}
-#endif
/**
* ab8500_gpadc_parse_channel() - process devicetree channel configuration
@@ -1199,20 +1197,16 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
return 0;
}
-static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
- ab8500_gpadc_runtime_resume,
- NULL)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops,
+ ab8500_gpadc_runtime_suspend,
+ ab8500_gpadc_runtime_resume, NULL);
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
.remove = ab8500_gpadc_remove,
.driver = {
.name = "ab8500-gpadc",
- .pm = &ab8500_gpadc_pm_ops,
+ .pm = pm_ptr(&ab8500_gpadc_pm_ops),
},
};
builtin_platform_driver(ab8500_gpadc_driver);
diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c
index 63b4d6ea4566..8e252cde735b 100644
--- a/drivers/iio/adc/ad7091r-base.c
+++ b/drivers/iio/adc/ad7091r-base.c
@@ -260,7 +260,7 @@ int ad7091r_probe(struct device *dev, const char *name,
return devm_iio_device_register(dev, iio_dev);
}
-EXPORT_SYMBOL_GPL(ad7091r_probe);
+EXPORT_SYMBOL_NS_GPL(ad7091r_probe, IIO_AD7091R);
static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg)
{
@@ -290,7 +290,7 @@ const struct regmap_config ad7091r_regmap_config = {
.writeable_reg = ad7091r_writeable_reg,
.volatile_reg = ad7091r_volatile_reg,
};
-EXPORT_SYMBOL_GPL(ad7091r_regmap_config);
+EXPORT_SYMBOL_NS_GPL(ad7091r_regmap_config, IIO_AD7091R);
MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters");
diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c
index 9665679c3ea6..47f5763023a4 100644
--- a/drivers/iio/adc/ad7091r5.c
+++ b/drivers/iio/adc/ad7091r5.c
@@ -111,3 +111,4 @@ module_i2c_driver(ad7091r5_driver);
MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD7091R);
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index b400bbe291aa..c47ead15f6e5 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -970,3 +970,4 @@ module_spi_driver(ad71124_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index cc990205f306..770b4e59238f 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -433,7 +433,7 @@ static ssize_t ad7192_show_ac_excitation(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7192_state *st = iio_priv(indio_dev);
- return sprintf(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX));
+ return sysfs_emit(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX));
}
static ssize_t ad7192_show_bridge_switch(struct device *dev,
@@ -443,7 +443,7 @@ static ssize_t ad7192_show_bridge_switch(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7192_state *st = iio_priv(indio_dev);
- return sprintf(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW));
+ return sysfs_emit(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW));
}
static ssize_t ad7192_set(struct device *dev,
@@ -1048,3 +1048,4 @@ module_spi_driver(ad7192_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7193, AD7195 ADC");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
new file mode 100644
index 000000000000..ef9d27759961
--- /dev/null
+++ b/drivers/iio/adc/ad7280a.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7280A Lithium Ion Battery Monitoring System
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+
+/* Registers */
+
+#define AD7280A_CELL_VOLTAGE_1_REG 0x0 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_2_REG 0x1 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_3_REG 0x2 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_4_REG 0x3 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_5_REG 0x4 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_6_REG 0x5 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_1_REG 0x6 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_2_REG 0x7 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_3_REG 0x8 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_4_REG 0x9 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_5_REG 0xA /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_6_REG 0xB /* D11 to D0, Read only */
+#define AD7280A_SELF_TEST_REG 0xC /* D11 to D0, Read only */
+
+#define AD7280A_CTRL_HB_REG 0xD /* D15 to D8, Read/write */
+#define AD7280A_CTRL_HB_CONV_INPUT_MSK GENMASK(7, 6)
+#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_5 1
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL 2
+#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST 3
+#define AD7280A_CTRL_HB_CONV_RREAD_MSK GENMASK(5, 4)
+#define AD7280A_CTRL_HB_CONV_RREAD_ALL 0
+#define AD7280A_CTRL_HB_CONV_RREAD_6CELL_AUX1_3_5 1
+#define AD7280A_CTRL_HB_CONV_RREAD_6CELL 2
+#define AD7280A_CTRL_HB_CONV_RREAD_NO 3
+#define AD7280A_CTRL_HB_CONV_START_MSK BIT(3)
+#define AD7280A_CTRL_HB_CONV_START_CNVST 0
+#define AD7280A_CTRL_HB_CONV_START_CS 1
+#define AD7280A_CTRL_HB_CONV_AVG_MSK GENMASK(2, 1)
+#define AD7280A_CTRL_HB_CONV_AVG_DIS 0
+#define AD7280A_CTRL_HB_CONV_AVG_2 1
+#define AD7280A_CTRL_HB_CONV_AVG_4 2
+#define AD7280A_CTRL_HB_CONV_AVG_8 3
+#define AD7280A_CTRL_HB_PWRDN_SW BIT(0)
+
+#define AD7280A_CTRL_LB_REG 0xE /* D7 to D0, Read/write */
+#define AD7280A_CTRL_LB_SWRST_MSK BIT(7)
+#define AD7280A_CTRL_LB_ACQ_TIME_MSK GENMASK(6, 5)
+#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0
+#define AD7280A_CTRL_LB_ACQ_TIME_800ns 1
+#define AD7280A_CTRL_LB_ACQ_TIME_1200ns 2
+#define AD7280A_CTRL_LB_ACQ_TIME_1600ns 3
+#define AD7280A_CTRL_LB_MUST_SET BIT(4)
+#define AD7280A_CTRL_LB_THERMISTOR_MSK BIT(3)
+#define AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK BIT(2)
+#define AD7280A_CTRL_LB_INC_DEV_ADDR_MSK BIT(1)
+#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK BIT(0)
+
+#define AD7280A_CELL_OVERVOLTAGE_REG 0xF /* D7 to D0, Read/write */
+#define AD7280A_CELL_UNDERVOLTAGE_REG 0x10 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_OVERVOLTAGE_REG 0x11 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_UNDERVOLTAGE_REG 0x12 /* D7 to D0, Read/write */
+
+#define AD7280A_ALERT_REG 0x13 /* D7 to D0, Read/write */
+#define AD7280A_ALERT_REMOVE_MSK GENMASK(3, 0)
+#define AD7280A_ALERT_REMOVE_AUX5 BIT(0)
+#define AD7280A_ALERT_REMOVE_AUX3_AUX5 BIT(1)
+#define AD7280A_ALERT_REMOVE_VIN5 BIT(2)
+#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3)
+#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6)
+#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6))
+
+#define AD7280A_CELL_BALANCE_REG 0x14 /* D7 to D0, Read/write */
+#define AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK GENMASK(7, 2)
+#define AD7280A_CB1_TIMER_REG 0x15 /* D7 to D0, Read/write */
+#define AD7280A_CB_TIMER_VAL_MSK GENMASK(7, 3)
+#define AD7280A_CB2_TIMER_REG 0x16 /* D7 to D0, Read/write */
+#define AD7280A_CB3_TIMER_REG 0x17 /* D7 to D0, Read/write */
+#define AD7280A_CB4_TIMER_REG 0x18 /* D7 to D0, Read/write */
+#define AD7280A_CB5_TIMER_REG 0x19 /* D7 to D0, Read/write */
+#define AD7280A_CB6_TIMER_REG 0x1A /* D7 to D0, Read/write */
+#define AD7280A_PD_TIMER_REG 0x1B /* D7 to D0, Read/write */
+#define AD7280A_READ_REG 0x1C /* D7 to D0, Read/write */
+#define AD7280A_READ_ADDR_MSK GENMASK(7, 2)
+#define AD7280A_CNVST_CTRL_REG 0x1D /* D7 to D0, Read/write */
+
+/* Transfer fields */
+#define AD7280A_TRANS_WRITE_DEVADDR_MSK GENMASK(31, 27)
+#define AD7280A_TRANS_WRITE_ADDR_MSK GENMASK(26, 21)
+#define AD7280A_TRANS_WRITE_VAL_MSK GENMASK(20, 13)
+#define AD7280A_TRANS_WRITE_ALL_MSK BIT(12)
+#define AD7280A_TRANS_WRITE_CRC_MSK GENMASK(10, 3)
+#define AD7280A_TRANS_WRITE_RES_PATTERN 0x2
+
+/* Layouts differ for channel vs other registers */
+#define AD7280A_TRANS_READ_DEVADDR_MSK GENMASK(31, 27)
+#define AD7280A_TRANS_READ_CONV_CHANADDR_MSK GENMASK(26, 23)
+#define AD7280A_TRANS_READ_CONV_DATA_MSK GENMASK(22, 11)
+#define AD7280A_TRANS_READ_REG_REGADDR_MSK GENMASK(26, 21)
+#define AD7280A_TRANS_READ_REG_DATA_MSK GENMASK(20, 13)
+#define AD7280A_TRANS_READ_WRITE_ACK_MSK BIT(10)
+#define AD7280A_TRANS_READ_CRC_MSK GENMASK(9, 2)
+
+/* Magic value used to indicate this special case */
+#define AD7280A_ALL_CELLS (0xAD << 16)
+
+#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */
+#define AD7280A_MAX_CHAIN 8
+#define AD7280A_CELLS_PER_DEV 6
+#define AD7280A_BITS 12
+#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6_REG - \
+ AD7280A_CELL_VOLTAGE_1_REG + 1)
+
+#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
+ (c))
+#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
+ (c) - AD7280A_CELLS_PER_DEV)
+
+#define AD7280A_DEVADDR_MASTER 0
+#define AD7280A_DEVADDR_ALL 0x1F
+
+static const unsigned short ad7280a_n_avg[4] = {1, 2, 4, 8};
+static const unsigned short ad7280a_t_acq_ns[4] = {470, 1030, 1510, 1945};
+
+/* 5-bit device address is sent LSB first */
+static unsigned int ad7280a_devaddr(unsigned int addr)
+{
+ return ((addr & 0x1) << 4) |
+ ((addr & 0x2) << 2) |
+ (addr & 0x4) |
+ ((addr & 0x8) >> 2) |
+ ((addr & 0x10) >> 4);
+}
+
+/*
+ * During a read a valid write is mandatory.
+ * So writing to the highest available address (Address 0x1F) and setting the
+ * address all parts bit to 0 is recommended.
+ * So the TXVAL is AD7280A_DEVADDR_ALL + CRC
+ */
+#define AD7280A_READ_TXVAL 0xF800030A
+
+/*
+ * AD7280 CRC
+ *
+ * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F
+ */
+#define POLYNOM 0x2F
+
+struct ad7280_state {
+ struct spi_device *spi;
+ struct iio_chan_spec *channels;
+ unsigned int chain_last_alert_ignore;
+ bool thermistor_term_en;
+ int slave_num;
+ int scan_cnt;
+ int readback_delay_us;
+ unsigned char crc_tab[CRC8_TABLE_SIZE];
+ u8 oversampling_ratio;
+ u8 acquisition_time;
+ unsigned char ctrl_lb;
+ unsigned char cell_threshhigh;
+ unsigned char cell_threshlow;
+ unsigned char aux_threshhigh;
+ unsigned char aux_threshlow;
+ unsigned char cb_mask[AD7280A_MAX_CHAIN];
+ struct mutex lock; /* protect sensor state */
+
+ __be32 tx ____cacheline_aligned;
+ __be32 rx;
+};
+
+static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val)
+{
+ unsigned char crc;
+
+ crc = crc_tab[val >> 16 & 0xFF];
+ crc = crc_tab[crc ^ (val >> 8 & 0xFF)];
+
+ return crc ^ (val & 0xFF);
+}
+
+static int ad7280_check_crc(struct ad7280_state *st, unsigned int val)
+{
+ unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10);
+
+ if (crc != ((val >> 2) & 0xFF))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * After initiating a conversion sequence we need to wait until the conversion
+ * is done. The delay is typically in the range of 15..30us however depending on
+ * the number of devices in the daisy chain, the number of averages taken,
+ * conversion delays and acquisition time options it may take up to 250us, in
+ * this case we better sleep instead of busy wait.
+ */
+
+static void ad7280_delay(struct ad7280_state *st)
+{
+ if (st->readback_delay_us < 50)
+ udelay(st->readback_delay_us);
+ else
+ usleep_range(250, 500);
+}
+
+static int __ad7280_read32(struct ad7280_state *st, unsigned int *val)
+{
+ int ret;
+ struct spi_transfer t = {
+ .tx_buf = &st->tx,
+ .rx_buf = &st->rx,
+ .len = sizeof(st->tx),
+ };
+
+ st->tx = cpu_to_be32(AD7280A_READ_TXVAL);
+
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(st->rx);
+
+ return 0;
+}
+
+static int ad7280_write(struct ad7280_state *st, unsigned int devaddr,
+ unsigned int addr, bool all, unsigned int val)
+{
+ unsigned int reg = FIELD_PREP(AD7280A_TRANS_WRITE_DEVADDR_MSK, devaddr) |
+ FIELD_PREP(AD7280A_TRANS_WRITE_ADDR_MSK, addr) |
+ FIELD_PREP(AD7280A_TRANS_WRITE_VAL_MSK, val) |
+ FIELD_PREP(AD7280A_TRANS_WRITE_ALL_MSK, all);
+
+ reg |= FIELD_PREP(AD7280A_TRANS_WRITE_CRC_MSK,
+ ad7280_calc_crc8(st->crc_tab, reg >> 11));
+ /* Reserved b010 pattern not included crc calc */
+ reg |= AD7280A_TRANS_WRITE_RES_PATTERN;
+
+ st->tx = cpu_to_be32(reg);
+
+ return spi_write(st->spi, &st->tx, sizeof(st->tx));
+}
+
+static int ad7280_read_reg(struct ad7280_state *st, unsigned int devaddr,
+ unsigned int addr)
+{
+ int ret;
+ unsigned int tmp;
+
+ /* turns off the read operation on all parts */
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_NO) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ /* turns on the read operation on the addressed part */
+ ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ /* Set register address on the part to be read from */
+ ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, addr));
+ if (ret)
+ return ret;
+
+ ret = __ad7280_read32(st, &tmp);
+ if (ret)
+ return ret;
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) ||
+ (FIELD_GET(AD7280A_TRANS_READ_REG_REGADDR_MSK, tmp) != addr))
+ return -EFAULT;
+
+ return FIELD_GET(AD7280A_TRANS_READ_REG_DATA_MSK, tmp);
+}
+
+static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr,
+ unsigned int addr)
+{
+ int ret;
+ unsigned int tmp;
+
+ ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, addr));
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_NO) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK,
+ AD7280A_CTRL_HB_CONV_START_CS) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ ad7280_delay(st);
+
+ ret = __ad7280_read32(st, &tmp);
+ if (ret)
+ return ret;
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) ||
+ (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) != addr))
+ return -EFAULT;
+
+ return FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp);
+}
+
+static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
+ unsigned int *array)
+{
+ int i, ret;
+ unsigned int tmp, sum = 0;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1,
+ AD7280A_CELL_VOLTAGE_1_REG << 2);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK,
+ AD7280A_CTRL_HB_CONV_START_CS) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ ad7280_delay(st);
+
+ for (i = 0; i < cnt; i++) {
+ ret = __ad7280_read32(st, &tmp);
+ if (ret)
+ return ret;
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if (array)
+ array[i] = tmp;
+ /* only sum cell voltages */
+ if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) <=
+ AD7280A_CELL_VOLTAGE_6_REG)
+ sum += FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp);
+ }
+
+ return sum;
+}
+
+static void ad7280_sw_power_down(void *data)
+{
+ struct ad7280_state *st = data;
+
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ AD7280A_CTRL_HB_PWRDN_SW |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio));
+}
+
+static int ad7280_chain_setup(struct ad7280_state *st)
+{
+ unsigned int val, n;
+ int ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) |
+ FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) |
+ AD7280A_CTRL_LB_MUST_SET |
+ FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 1) |
+ st->ctrl_lb);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) |
+ FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) |
+ AD7280A_CTRL_LB_MUST_SET |
+ FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 0) |
+ st->ctrl_lb);
+ if (ret)
+ goto error_power_down;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, AD7280A_CTRL_LB_REG));
+ if (ret)
+ goto error_power_down;
+
+ for (n = 0; n <= AD7280A_MAX_CHAIN; n++) {
+ ret = __ad7280_read32(st, &val);
+ if (ret)
+ goto error_power_down;
+
+ if (val == 0)
+ return n - 1;
+
+ if (ad7280_check_crc(st, val)) {
+ ret = -EIO;
+ goto error_power_down;
+ }
+
+ if (n != ad7280a_devaddr(FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, val))) {
+ ret = -EIO;
+ goto error_power_down;
+ }
+ }
+ ret = -EFAULT;
+
+error_power_down:
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ AD7280A_CTRL_HB_PWRDN_SW |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio));
+
+ return ret;
+}
+
+static ssize_t ad7280_show_balance_sw(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+
+ return sysfs_emit(buf, "%d\n",
+ !!(st->cb_mask[chan->address >> 8] &
+ BIT(chan->address & 0xFF)));
+}
+
+static ssize_t ad7280_store_balance_sw(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned int devaddr, ch;
+ bool readin;
+ int ret;
+
+ ret = strtobool(buf, &readin);
+ if (ret)
+ return ret;
+
+ devaddr = chan->address >> 8;
+ ch = chan->address & 0xFF;
+
+ mutex_lock(&st->lock);
+ if (readin)
+ st->cb_mask[devaddr] |= BIT(ch);
+ else
+ st->cb_mask[devaddr] &= ~BIT(ch);
+
+ ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE_REG, 0,
+ FIELD_PREP(AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK,
+ st->cb_mask[devaddr]));
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static ssize_t ad7280_show_balance_timer(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned int msecs;
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad7280_read_reg(st, chan->address >> 8,
+ (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG);
+ mutex_unlock(&st->lock);
+
+ if (ret < 0)
+ return ret;
+
+ msecs = FIELD_GET(AD7280A_CB_TIMER_VAL_MSK, ret) * 71500;
+
+ return sysfs_emit(buf, "%u.%u\n", msecs / 1000, msecs % 1000);
+}
+
+static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int val, val2;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 1000, &val, &val2);
+ if (ret)
+ return ret;
+
+ val = val * 1000 + val2;
+ val /= 71500;
+
+ if (val > 31)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = ad7280_write(st, chan->address >> 8,
+ (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG, 0,
+ FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val));
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ad7280_cell_ext_info[] = {
+ {
+ .name = "balance_switch_en",
+ .read = ad7280_show_balance_sw,
+ .write = ad7280_store_balance_sw,
+ .shared = IIO_SEPARATE,
+ }, {
+ .name = "balance_switch_timer",
+ .read = ad7280_show_balance_timer,
+ .write = ad7280_store_balance_timer,
+ .shared = IIO_SEPARATE,
+ },
+ {}
+};
+
+static const struct iio_event_spec ad7280_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i,
+ bool irq_present)
+{
+ chan->type = IIO_VOLTAGE;
+ chan->differential = 1;
+ chan->channel = i;
+ chan->channel2 = chan->channel + 1;
+ if (irq_present) {
+ chan->event_spec = ad7280_events;
+ chan->num_event_specs = ARRAY_SIZE(ad7280_events);
+ }
+ chan->ext_info = ad7280_cell_ext_info;
+}
+
+static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i,
+ bool irq_present)
+{
+ chan->type = IIO_TEMP;
+ chan->channel = i;
+ if (irq_present) {
+ chan->event_spec = ad7280_events;
+ chan->num_event_specs = ARRAY_SIZE(ad7280_events);
+ }
+}
+
+static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr,
+ int cnt)
+{
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ chan->address = addr;
+ chan->scan_index = cnt;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 12;
+ chan->scan_type.storagebits = 32;
+}
+
+static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan,
+ int cnt, int dev)
+{
+ chan->type = IIO_VOLTAGE;
+ chan->differential = 1;
+ chan->channel = 0;
+ chan->channel2 = dev * AD7280A_CELLS_PER_DEV;
+ chan->address = AD7280A_ALL_CELLS;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_index = cnt;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 32;
+ chan->scan_type.storagebits = 32;
+}
+
+static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt,
+ bool irq_present)
+{
+ int addr, ch, i;
+ struct iio_chan_spec *chan;
+
+ for (ch = AD7280A_CELL_VOLTAGE_1_REG; ch <= AD7280A_AUX_ADC_6_REG; ch++) {
+ chan = &st->channels[*cnt];
+
+ if (ch < AD7280A_AUX_ADC_1_REG) {
+ i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch);
+ ad7280_voltage_channel_init(chan, i, irq_present);
+ } else {
+ i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch);
+ ad7280_temp_channel_init(chan, i, irq_present);
+ }
+
+ addr = ad7280a_devaddr(dev) << 8 | ch;
+ ad7280_common_fields_init(chan, addr, *cnt);
+
+ (*cnt)++;
+ }
+}
+
+static int ad7280_channel_init(struct ad7280_state *st, bool irq_present)
+{
+ int dev, cnt = 0;
+
+ st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 1,
+ sizeof(*st->channels), GFP_KERNEL);
+ if (!st->channels)
+ return -ENOMEM;
+
+ for (dev = 0; dev <= st->slave_num; dev++)
+ ad7280_init_dev_channels(st, dev, &cnt, irq_present);
+
+ ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev);
+
+ return cnt + 1;
+}
+
+static int ad7280a_read_thresh(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 ad7280_state *st = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = 1000 + (st->cell_threshhigh * 1568L) / 100;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ *val = 1000 + (st->cell_threshlow * 1568L) / 100;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_TEMP:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = ((st->aux_threshhigh) * 196L) / 10;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ *val = (st->aux_threshlow * 196L) / 10;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7280a_write_thresh(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 ad7280_state *st = iio_priv(indio_dev);
+ unsigned int addr;
+ long value;
+ int ret;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ value = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */
+ value = clamp(value, 0L, 0xFFL);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ addr = AD7280A_CELL_OVERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, val);
+ if (ret)
+ break;
+ st->cell_threshhigh = value;
+ break;
+ case IIO_EV_DIR_FALLING:
+ addr = AD7280A_CELL_UNDERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, val);
+ if (ret)
+ break;
+ st->cell_threshlow = value;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ break;
+ case IIO_TEMP:
+ value = (val * 10) / 196; /* LSB 19.6mV */
+ value = clamp(value, 0L, 0xFFL);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ addr = AD7280A_AUX_ADC_OVERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, val);
+ if (ret)
+ break;
+ st->aux_threshhigh = val;
+ break;
+ case IIO_EV_DIR_FALLING:
+ addr = AD7280A_AUX_ADC_UNDERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, val);
+ if (ret)
+ break;
+ st->aux_threshlow = val;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static irqreturn_t ad7280_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned int *channels;
+ int i, ret;
+
+ channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return IRQ_HANDLED;
+
+ ret = ad7280_read_all_channels(st, st->scan_cnt, channels);
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < st->scan_cnt; i++) {
+ unsigned int val;
+
+ val = FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, channels[i]);
+ if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, channels[i]) <=
+ AD7280A_CELL_VOLTAGE_6_REG) {
+ if (val >= st->cell_threshhigh) {
+ u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ } else if (val <= st->cell_threshlow) {
+ u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
+ IIO_EV_DIR_FALLING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ }
+ } else {
+ if (val >= st->aux_threshhigh) {
+ u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ } else if (val <= st->aux_threshlow) {
+ u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ }
+ }
+ }
+
+out:
+ kfree(channels);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7280_update_delay(struct ad7280_state *st)
+{
+ /*
+ * Total Conversion Time = ((tACQ + tCONV) *
+ * (Number of Conversions per Part)) −
+ * tACQ + ((N - 1) * tDELAY)
+ *
+ * Readback Delay = Total Conversion Time + tWAIT
+ */
+
+ st->readback_delay_us =
+ ((ad7280a_t_acq_ns[st->acquisition_time & 0x3] + 720) *
+ (AD7280A_NUM_CH * ad7280a_n_avg[st->oversampling_ratio & 0x3])) -
+ ad7280a_t_acq_ns[st->acquisition_time & 0x3] + st->slave_num * 250;
+
+ /* Convert to usecs */
+ st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000);
+ st->readback_delay_us += 5; /* Add tWAIT */
+}
+
+static int ad7280_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ if (chan->address == AD7280A_ALL_CELLS)
+ ret = ad7280_read_all_channels(st, st->scan_cnt, NULL);
+ else
+ ret = ad7280_read_channel(st, chan->address >> 8,
+ chan->address & 0xFF);
+ mutex_unlock(&st->lock);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6_REG)
+ *val = 4000;
+ else
+ *val = 5000;
+
+ *val2 = AD7280A_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = ad7280a_n_avg[st->oversampling_ratio];
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int ad7280_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (val2 != 0)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(ad7280a_n_avg); i++) {
+ if (val == ad7280a_n_avg[i]) {
+ st->oversampling_ratio = i;
+ ad7280_update_delay(st);
+ return 0;
+ }
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7280_info = {
+ .read_raw = ad7280_read_raw,
+ .write_raw = ad7280_write_raw,
+ .read_event_value = &ad7280a_read_thresh,
+ .write_event_value = &ad7280a_write_thresh,
+};
+
+static const struct iio_info ad7280_info_no_irq = {
+ .read_raw = ad7280_read_raw,
+ .write_raw = ad7280_write_raw,
+};
+
+static int ad7280_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ad7280_state *st;
+ int ret;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+ mutex_init(&st->lock);
+
+ st->thermistor_term_en =
+ device_property_read_bool(dev, "adi,thermistor-termination");
+
+ if (device_property_present(dev, "adi,acquisition-time-ns")) {
+ u32 val;
+
+ ret = device_property_read_u32(dev, "adi,acquisition-time-ns", &val);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 400:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns;
+ break;
+ case 800:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_800ns;
+ break;
+ case 1200:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1200ns;
+ break;
+ case 1600:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1600ns;
+ break;
+ default:
+ dev_err(dev, "Firmware provided acquisition time is invalid\n");
+ return -EINVAL;
+ }
+ } else {
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns;
+ }
+
+ /* Alert masks are intended for when particular inputs are not wired up */
+ if (device_property_present(dev, "adi,voltage-alert-last-chan")) {
+ u32 val;
+
+ ret = device_property_read_u32(dev, "adi,voltage-alert-last-chan", &val);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 3:
+ st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN4_VIN5;
+ break;
+ case 4:
+ st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN5;
+ break;
+ case 5:
+ break;
+ default:
+ dev_err(dev,
+ "Firmware provided last voltage alert channel invalid\n");
+ break;
+ }
+ }
+ crc8_populate_msb(st->crc_tab, POLYNOM);
+
+ st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ;
+ st->spi->mode = SPI_MODE_1;
+ spi_setup(st->spi);
+
+ st->ctrl_lb = FIELD_PREP(AD7280A_CTRL_LB_ACQ_TIME_MSK, st->acquisition_time) |
+ FIELD_PREP(AD7280A_CTRL_LB_THERMISTOR_MSK, st->thermistor_term_en);
+ st->oversampling_ratio = 0; /* No oversampling */
+
+ ret = ad7280_chain_setup(st);
+ if (ret < 0)
+ return ret;
+
+ st->slave_num = ret;
+ st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH;
+ st->cell_threshhigh = 0xFF;
+ st->aux_threshhigh = 0xFF;
+
+ ret = devm_add_action_or_reset(dev, ad7280_sw_power_down, st);
+ if (ret)
+ return ret;
+
+ ad7280_update_delay(st);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad7280_channel_init(st, spi->irq > 0);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->num_channels = ret;
+ indio_dev->channels = st->channels;
+ if (spi->irq > 0) {
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
+ AD7280A_ALERT_REG, 1,
+ AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, ad7280a_devaddr(st->slave_num),
+ AD7280A_ALERT_REG, 0,
+ AD7280A_ALERT_GEN_STATIC_HIGH |
+ FIELD_PREP(AD7280A_ALERT_REMOVE_MSK,
+ st->chain_last_alert_ignore));
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, spi->irq,
+ NULL,
+ ad7280_event_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &ad7280_info;
+ } else {
+ indio_dev->info = &ad7280_info_no_irq;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad7280_id[] = {
+ {"ad7280a", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7280_id);
+
+static struct spi_driver ad7280_driver = {
+ .driver = {
+ .name = "ad7280",
+ },
+ .probe = ad7280_probe,
+ .id_table = ad7280_id,
+};
+module_spi_driver(ad7280_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7280A");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index 0a60ecc69d38..3b193dc26438 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -693,7 +693,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(ad7606_probe);
+EXPORT_SYMBOL_NS_GPL(ad7606_probe, IIO_AD7606);
#ifdef CONFIG_PM_SLEEP
@@ -725,7 +725,7 @@ static int ad7606_resume(struct device *dev)
}
SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume);
-EXPORT_SYMBOL_GPL(ad7606_pm_ops);
+EXPORT_SYMBOL_NS_GPL(ad7606_pm_ops, IIO_AD7606);
#endif
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
index f732b3ac7878..8888e56b5e90 100644
--- a/drivers/iio/adc/ad7606_par.c
+++ b/drivers/iio/adc/ad7606_par.c
@@ -101,3 +101,4 @@ module_platform_driver(ad7606_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD7606);
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index 29945ad07dca..263a778bcf25 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -362,3 +362,4 @@ module_spi_driver(ad7606_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD7606);
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
index b6e8c8abf6f4..a813fe04787c 100644
--- a/drivers/iio/adc/ad7780.c
+++ b/drivers/iio/adc/ad7780.c
@@ -375,3 +375,4 @@ module_spi_driver(ad7780_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
index cb579aa89f39..fee8d129a5f0 100644
--- a/drivers/iio/adc/ad7791.c
+++ b/drivers/iio/adc/ad7791.c
@@ -474,3 +474,4 @@ module_spi_driver(ad7791_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index 0e7ab3fb072a..5f8cb9aaac70 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -867,3 +867,4 @@ module_spi_driver(ad7793_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index cd418bd8bd87..ebcd52526cac 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -42,7 +42,7 @@ void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
* to select the channel */
sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
}
-EXPORT_SYMBOL_GPL(ad_sd_set_comm);
+EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, IIO_AD_SIGMA_DELTA);
/**
* ad_sd_write_reg() - Write a register
@@ -94,7 +94,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
return ret;
}
-EXPORT_SYMBOL_GPL(ad_sd_write_reg);
+EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, IIO_AD_SIGMA_DELTA);
static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
unsigned int reg, unsigned int size, uint8_t *val)
@@ -171,7 +171,7 @@ int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
out:
return ret;
}
-EXPORT_SYMBOL_GPL(ad_sd_read_reg);
+EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, IIO_AD_SIGMA_DELTA);
/**
* ad_sd_reset() - Reset the serial interface
@@ -199,7 +199,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
return ret;
}
-EXPORT_SYMBOL_GPL(ad_sd_reset);
+EXPORT_SYMBOL_NS_GPL(ad_sd_reset, IIO_AD_SIGMA_DELTA);
int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
unsigned int mode, unsigned int channel)
@@ -238,7 +238,7 @@ out:
return ret;
}
-EXPORT_SYMBOL_GPL(ad_sd_calibrate);
+EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, IIO_AD_SIGMA_DELTA);
/**
* ad_sd_calibrate_all() - Performs channel calibration
@@ -262,7 +262,7 @@ int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
return 0;
}
-EXPORT_SYMBOL_GPL(ad_sd_calibrate_all);
+EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, IIO_AD_SIGMA_DELTA);
/**
* ad_sigma_delta_single_conversion() - Performs a single data conversion
@@ -337,7 +337,7 @@ out:
return IIO_VAL_INT;
}
-EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion);
+EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, IIO_AD_SIGMA_DELTA);
static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
{
@@ -465,7 +465,7 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
return 0;
}
-EXPORT_SYMBOL_GPL(ad_sd_validate_trigger);
+EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA);
static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev)
{
@@ -524,7 +524,7 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi
return devm_ad_sd_probe_trigger(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(devm_ad_sd_setup_buffer_and_trigger);
+EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, IIO_AD_SIGMA_DELTA);
/**
* ad_sd_init() - Initializes a ad_sigma_delta struct
@@ -545,7 +545,7 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL_GPL(ad_sd_init);
+EXPORT_SYMBOL_NS_GPL(ad_sd_init, IIO_AD_SIGMA_DELTA);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index e939b84cbb56..0793d2474cdc 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -539,7 +539,9 @@ static int aspeed_adc_probe(struct platform_device *pdev)
data->clk_scaler = devm_clk_hw_register_divider(
&pdev->dev, clk_name, clk_parent_name, scaler_flags,
data->base + ASPEED_REG_CLOCK_CONTROL, 0,
- data->model_data->scaler_bit_width, 0, &data->clk_lock);
+ data->model_data->scaler_bit_width,
+ data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0,
+ &data->clk_lock);
if (IS_ERR(data->clk_scaler))
return PTR_ERR(data->clk_scaler);
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 5a7d3a3a5fa8..532daaa6f943 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -1234,7 +1234,6 @@ static int at91_adc_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int at91_adc_suspend(struct device *dev)
{
struct iio_dev *idev = dev_get_drvdata(dev);
@@ -1256,9 +1255,9 @@ static int at91_adc_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend,
+ at91_adc_resume);
static const struct at91_adc_trigger at91sam9260_triggers[] = {
{ .name = "timer-counter-0", .value = 0x1 },
@@ -1386,7 +1385,7 @@ static struct platform_driver at91_adc_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = at91_adc_dt_ids,
- .pm = &at91_adc_pm_ops,
+ .pm = pm_sleep_ptr(&at91_adc_pm_ops),
},
};
diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c
index 40e59f4c95bc..b6c4ef70484e 100644
--- a/drivers/iio/adc/cpcap-adc.c
+++ b/drivers/iio/adc/cpcap-adc.c
@@ -474,7 +474,7 @@ static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata,
for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) {
calibration_data[0] = 0;
calibration_data[1] = 0;
- cal_data_diff = 0;
+
cpcap_adc_setup_calibrate(ddata, channel);
error = regmap_read(ddata->reg, calibration_register,
&calibration_data[0]);
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 3b3868aa2533..cff1ba57fb16 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -968,7 +968,6 @@ static int exynos_adc_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int exynos_adc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -1001,11 +1000,9 @@ static int exynos_adc_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops,
- exynos_adc_suspend,
- exynos_adc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend,
+ exynos_adc_resume);
static struct platform_driver exynos_adc_driver = {
.probe = exynos_adc_probe,
@@ -1013,7 +1010,7 @@ static struct platform_driver exynos_adc_driver = {
.driver = {
.name = "exynos-adc",
.of_match_table = exynos_adc_match,
- .pm = &exynos_adc_pm_ops,
+ .pm = pm_sleep_ptr(&exynos_adc_pm_ops),
},
};
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index e665e14c6e54..8eb0140df133 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -529,7 +529,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},
+ { "hi8435", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, hi8435_id);
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index 4f9992a51e64..8d902a32a0fd 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -539,7 +539,7 @@ static ssize_t ina2xx_allow_async_readout_show(struct device *dev,
{
struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
- return sprintf(buf, "%d\n", chip->allow_async_readout);
+ return sysfs_emit(buf, "%d\n", chip->allow_async_readout);
}
static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 01a4275e9c46..f982f00303dc 100644
--- a/drivers/iio/adc/max9611.c
+++ b/drivers/iio/adc/max9611.c
@@ -429,7 +429,7 @@ static ssize_t max9611_shunt_resistor_show(struct device *dev,
i = max9611->shunt_resistor_uohm / 1000000;
r = max9611->shunt_resistor_uohm % 1000000;
- return sprintf(buf, "%u.%06u\n", i, r);
+ return sysfs_emit(buf, "%u.%06u\n", i, r);
}
static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444,
diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c
index d4fccd52ef08..e78c96a185db 100644
--- a/drivers/iio/adc/mt6577_auxadc.c
+++ b/drivers/iio/adc/mt6577_auxadc.c
@@ -46,6 +46,11 @@ struct mt6577_auxadc_device {
const struct mtk_auxadc_compatible *dev_comp;
};
+static const struct mtk_auxadc_compatible mt8186_compat = {
+ .sample_data_cali = false,
+ .check_global_idle = false,
+};
+
static const struct mtk_auxadc_compatible mt8173_compat = {
.sample_data_cali = false,
.check_global_idle = true,
@@ -330,11 +335,12 @@ static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops,
mt6577_auxadc_resume);
static const struct of_device_id mt6577_auxadc_of_match[] = {
- { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat},
- { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat},
- { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat},
- { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat},
- { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat},
+ { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt8186-auxadc", .data = &mt8186_compat },
+ { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat },
{ }
};
MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c
index f9c8385c72d3..61e80bf3d05e 100644
--- a/drivers/iio/adc/palmas_gpadc.c
+++ b/drivers/iio/adc/palmas_gpadc.c
@@ -653,7 +653,6 @@ static int palmas_gpadc_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc)
{
int adc_period, conv;
@@ -822,12 +821,9 @@ static int palmas_gpadc_resume(struct device *dev)
return 0;
};
-#endif
-static const struct dev_pm_ops palmas_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(palmas_gpadc_suspend,
- palmas_gpadc_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_gpadc_suspend,
+ palmas_gpadc_resume);
static const struct of_device_id of_palmas_gpadc_match_tbl[] = {
{ .compatible = "ti,palmas-gpadc", },
@@ -840,7 +836,7 @@ static struct platform_driver palmas_gpadc_driver = {
.remove = palmas_gpadc_remove,
.driver = {
.name = MOD_NAME,
- .pm = &palmas_pm_ops,
+ .pm = pm_sleep_ptr(&palmas_pm_ops),
.of_match_table = of_palmas_gpadc_match_tbl,
},
};
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
index 21d7eff645c3..5e9e56821075 100644
--- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -175,7 +175,7 @@ struct xoadc_channel {
const char *datasheet_name;
u8 pre_scale_mux:2;
u8 amux_channel:4;
- const struct vadc_prescale_ratio prescale;
+ const struct u32_fract prescale;
enum iio_chan_type type;
enum vadc_scale_fn_type scale_fn_type;
u8 amux_ip_rsv:3;
@@ -218,7 +218,9 @@ struct xoadc_variant {
.datasheet_name = __stringify(_dname), \
.pre_scale_mux = _presmux, \
.amux_channel = _amux, \
- .prescale = { .num = _prenum, .den = _preden }, \
+ .prescale = { \
+ .numerator = _prenum, .denominator = _preden, \
+ }, \
.type = _type, \
.scale_fn_type = _scale, \
.amux_ip_rsv = _amip, \
@@ -809,12 +811,11 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev,
BIT(IIO_CHAN_INFO_PROCESSED);
iio_chan->indexed = 1;
- dev_dbg(dev, "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" "
- "ref voltage: %d, decimation %d "
- "prescale %d/%d, scale function %d\n",
+ dev_dbg(dev,
+ "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" ref voltage: %d, decimation %d prescale %d/%d, scale function %d\n",
hwchan->pre_scale_mux, hwchan->amux_channel, ch->name,
- ch->amux_ip_rsv, ch->decimation, hwchan->prescale.num,
- hwchan->prescale.den, hwchan->scale_fn_type);
+ ch->amux_ip_rsv, ch->decimation, hwchan->prescale.numerator,
+ hwchan->prescale.denominator, hwchan->scale_fn_type);
return 0;
}
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 07b1a99381d9..34202ba52469 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -122,15 +122,15 @@ struct vadc_priv {
struct mutex lock;
};
-static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
- {.num = 1, .den = 1},
- {.num = 1, .den = 3},
- {.num = 1, .den = 4},
- {.num = 1, .den = 6},
- {.num = 1, .den = 20},
- {.num = 1, .den = 8},
- {.num = 10, .den = 81},
- {.num = 1, .den = 10}
+static const struct u32_fract vadc_prescale_ratios[] = {
+ { .numerator = 1, .denominator = 1 },
+ { .numerator = 1, .denominator = 3 },
+ { .numerator = 1, .denominator = 4 },
+ { .numerator = 1, .denominator = 6 },
+ { .numerator = 1, .denominator = 20 },
+ { .numerator = 1, .denominator = 8 },
+ { .numerator = 10, .denominator = 81 },
+ { .numerator = 1, .denominator = 10 },
};
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
@@ -404,13 +404,13 @@ err:
return ret;
}
-static int vadc_prescaling_from_dt(u32 num, u32 den)
+static int vadc_prescaling_from_dt(u32 numerator, u32 denominator)
{
unsigned int pre;
for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++)
- if (vadc_prescale_ratios[pre].num == num &&
- vadc_prescale_ratios[pre].den == den)
+ if (vadc_prescale_ratios[pre].numerator == numerator &&
+ vadc_prescale_ratios[pre].denominator == denominator)
break;
if (pre == ARRAY_SIZE(vadc_prescale_ratios))
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
index 14723896aab2..6c6aec848f98 100644
--- a/drivers/iio/adc/qcom-vadc-common.c
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -289,44 +289,44 @@ static const struct vadc_map_pt adcmap7_100k[] = {
{ 2420, 130048 }
};
-static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
- {.num = 1, .den = 1},
- {.num = 1, .den = 3},
- {.num = 1, .den = 4},
- {.num = 1, .den = 6},
- {.num = 1, .den = 20},
- {.num = 1, .den = 8},
- {.num = 10, .den = 81},
- {.num = 1, .den = 10},
- {.num = 1, .den = 16}
+static const struct u32_fract adc5_prescale_ratios[] = {
+ { .numerator = 1, .denominator = 1 },
+ { .numerator = 1, .denominator = 3 },
+ { .numerator = 1, .denominator = 4 },
+ { .numerator = 1, .denominator = 6 },
+ { .numerator = 1, .denominator = 20 },
+ { .numerator = 1, .denominator = 8 },
+ { .numerator = 10, .denominator = 81 },
+ { .numerator = 1, .denominator = 10 },
+ { .numerator = 1, .denominator = 16 },
};
static int qcom_vadc_scale_hw_calib_volt(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_uv);
static int qcom_vadc_scale_hw_calib_therm(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc7_scale_hw_calib_therm(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc_scale_hw_smb_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc_scale_hw_chg5_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc_scale_hw_calib_die_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc7_scale_hw_calib_die_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
@@ -406,7 +406,7 @@ static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
}
static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
bool absolute, u16 adc_code,
int *result_uv)
{
@@ -414,15 +414,15 @@ static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
- voltage = voltage * prescale->den;
- result = div64_s64(voltage, prescale->num);
+ voltage *= prescale->denominator;
+ result = div64_s64(voltage, prescale->numerator);
*result_uv = result;
return 0;
}
static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
bool absolute, u16 adc_code,
int *result_mdec)
{
@@ -444,7 +444,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
}
static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
bool absolute,
u16 adc_code, int *result_mdec)
{
@@ -454,8 +454,8 @@ static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
if (voltage > 0) {
- temp = voltage * prescale->den;
- do_div(temp, prescale->num * 2);
+ temp = voltage * prescale->denominator;
+ do_div(temp, prescale->numerator * 2);
voltage = temp;
} else {
voltage = 0;
@@ -467,7 +467,7 @@ static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
}
static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
bool absolute,
u16 adc_code, int *result_mdec)
{
@@ -475,8 +475,8 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
- voltage = voltage * prescale->den;
- voltage = div64_s64(voltage, prescale->num);
+ voltage *= prescale->denominator;
+ voltage = div64_s64(voltage, prescale->numerator);
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
voltage = (voltage + PMI_CHG_SCALE_2);
result = div64_s64(voltage, 1000000);
@@ -487,21 +487,21 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
/* convert voltage to ADC code, using 1.875V reference */
static u16 qcom_vadc_scale_voltage_code(s32 voltage,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const u32 full_scale_code_volt,
unsigned int factor)
{
s64 volt = voltage;
s64 adc_vdd_ref_mv = 1875; /* reference voltage */
- volt *= prescale->num * factor * full_scale_code_volt;
- volt = div64_s64(volt, (s64)prescale->den * adc_vdd_ref_mv * 1000);
+ volt *= prescale->numerator * factor * full_scale_code_volt;
+ volt = div64_s64(volt, (s64)prescale->denominator * adc_vdd_ref_mv * 1000);
return volt;
}
static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
unsigned int factor)
{
@@ -520,8 +520,8 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
voltage = div64_s64(voltage, data->full_scale_code_volt);
if (voltage > 0) {
- voltage *= prescale->den;
- temp = prescale->num * factor;
+ voltage *= prescale->denominator;
+ temp = prescale->numerator * factor;
voltage = div64_s64(voltage, temp);
} else {
voltage = 0;
@@ -531,7 +531,7 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
}
static int qcom_vadc7_scale_hw_calib_therm(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
@@ -557,7 +557,7 @@ static int qcom_vadc7_scale_hw_calib_therm(
}
static int qcom_vadc_scale_hw_calib_volt(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_uv)
{
@@ -568,7 +568,7 @@ static int qcom_vadc_scale_hw_calib_volt(
}
static int qcom_vadc_scale_hw_calib_therm(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
@@ -584,7 +584,7 @@ static int qcom_vadc_scale_hw_calib_therm(
}
static int qcom_vadc_scale_hw_calib_die_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
@@ -596,7 +596,7 @@ static int qcom_vadc_scale_hw_calib_die_temp(
}
static int qcom_vadc7_scale_hw_calib_die_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
@@ -611,7 +611,7 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
}
static int qcom_vadc_scale_hw_smb_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
@@ -623,7 +623,7 @@ static int qcom_vadc_scale_hw_smb_temp(
}
static int qcom_vadc_scale_hw_chg5_temp(
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
@@ -636,7 +636,7 @@ static int qcom_vadc_scale_hw_chg5_temp(
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
- const struct vadc_prescale_ratio *prescale,
+ const struct u32_fract *prescale,
bool absolute,
u16 adc_code, int *result)
{
@@ -667,7 +667,7 @@ EXPORT_SYMBOL(qcom_vadc_scale);
u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio,
u32 full_scale_code_volt, int temp)
{
- const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
+ const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio];
s32 voltage;
voltage = qcom_vadc_map_temp_voltage(adcmap_100k_104ef_104fb_1875_vref,
@@ -682,7 +682,7 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
const struct adc5_data *data,
u16 adc_code, int *result)
{
- const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
+ const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio];
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
scaletype < SCALE_HW_CALIB_INVALID)) {
@@ -695,13 +695,13 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
}
EXPORT_SYMBOL(qcom_adc5_hw_scale);
-int qcom_adc5_prescaling_from_dt(u32 num, u32 den)
+int qcom_adc5_prescaling_from_dt(u32 numerator, u32 denominator)
{
unsigned int pre;
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
- if (adc5_prescale_ratios[pre].num == num &&
- adc5_prescale_ratios[pre].den == den)
+ if (adc5_prescale_ratios[pre].numerator == numerator &&
+ adc5_prescale_ratios[pre].denominator == denominator)
break;
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index 727ea6c68049..27d9e147b4b7 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -577,7 +577,6 @@ static int rcar_gyroadc_remove(struct platform_device *pdev)
return 0;
}
-#if defined(CONFIG_PM)
static int rcar_gyroadc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -597,10 +596,9 @@ static int rcar_gyroadc_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops rcar_gyroadc_pm_ops = {
- SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
+ RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
};
static struct platform_driver rcar_gyroadc_driver = {
@@ -609,7 +607,7 @@ static struct platform_driver rcar_gyroadc_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = rcar_gyroadc_match,
- .pm = &rcar_gyroadc_pm_ops,
+ .pm = pm_ptr(&rcar_gyroadc_pm_ops),
},
};
diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c
index 7d891b4ea461..6bf32907f01d 100644
--- a/drivers/iio/adc/rn5t618-adc.c
+++ b/drivers/iio/adc/rn5t618-adc.c
@@ -42,11 +42,6 @@ struct rn5t618_adc_data {
int irq;
};
-struct rn5t618_channel_ratios {
- u16 numerator;
- u16 denominator;
-};
-
enum rn5t618_channels {
LIMMON = 0,
VBAT,
@@ -58,7 +53,7 @@ enum rn5t618_channels {
AIN0
};
-static const struct rn5t618_channel_ratios rn5t618_ratios[8] = {
+static const struct u16_fract rn5t618_ratios[8] = {
[LIMMON] = {50, 32}, /* measured across 20mOhm, amplified by 32 */
[VBAT] = {2, 1},
[VADP] = {3, 1},
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 14b8df4ca9c8..b87ea7148b58 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -481,7 +481,6 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
return devm_iio_device_register(&pdev->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int rockchip_saradc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -514,17 +513,17 @@ static int rockchip_saradc_resume(struct device *dev)
return ret;
}
-#endif
-static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
- rockchip_saradc_suspend, rockchip_saradc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
+ rockchip_saradc_suspend,
+ rockchip_saradc_resume);
static struct platform_driver rockchip_saradc_driver = {
.probe = rockchip_saradc_probe,
.driver = {
.name = "rockchip-saradc",
.of_match_table = rockchip_saradc_match,
- .pm = &rockchip_saradc_pm_ops,
+ .pm = pm_sleep_ptr(&rockchip_saradc_pm_ops),
},
};
diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
index 9d5be52bd948..7585144b9715 100644
--- a/drivers/iio/adc/rzg2l_adc.c
+++ b/drivers/iio/adc/rzg2l_adc.c
@@ -55,7 +55,7 @@
#define RZG2L_ADCR(n) (0x30 + ((n) * 0x4))
#define RZG2L_ADCR_AD_MASK GENMASK(11, 0)
-#define RZG2L_ADSMP_DEFUALT_SAMPLING 0x578
+#define RZG2L_ADSMP_DEFAULT_SAMPLING 0x578
#define RZG2L_ADC_MAX_CHANNELS 8
#define RZG2L_ADC_CHN_MASK 0x7
@@ -395,7 +395,7 @@ static int rzg2l_adc_hw_init(struct rzg2l_adc *adc)
reg &= ~RZG2L_ADM3_ADIL_MASK;
reg &= ~RZG2L_ADM3_ADCMP_MASK;
reg &= ~RZG2L_ADM3_ADSMP_MASK;
- reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFUALT_SAMPLING);
+ reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFAULT_SAMPLING);
rzg2l_adc_writel(adc, RZG2L_ADM(3), reg);
exit_hw_init:
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index b6e18eb101f7..142656232157 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -763,7 +763,6 @@ static int stm32_adc_remove(struct platform_device *pdev)
return 0;
}
-#if defined(CONFIG_PM)
static int stm32_adc_core_runtime_suspend(struct device *dev)
{
stm32_adc_core_hw_stop(dev);
@@ -782,15 +781,11 @@ static int stm32_adc_core_runtime_idle(struct device *dev)
return 0;
}
-#endif
-
-static const struct dev_pm_ops stm32_adc_core_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(stm32_adc_core_runtime_suspend,
- stm32_adc_core_runtime_resume,
- stm32_adc_core_runtime_idle)
-};
+
+static DEFINE_RUNTIME_DEV_PM_OPS(stm32_adc_core_pm_ops,
+ stm32_adc_core_runtime_suspend,
+ stm32_adc_core_runtime_resume,
+ stm32_adc_core_runtime_idle);
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
.regs = &stm32f4_adc_common_regs,
@@ -836,7 +831,7 @@ static struct platform_driver stm32_adc_driver = {
.driver = {
.name = "stm32-adc-core",
.of_match_table = stm32_adc_of_match,
- .pm = &stm32_adc_core_pm_ops,
+ .pm = pm_ptr(&stm32_adc_core_pm_ops),
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 897166d9e45c..a68ecbda6480 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -2352,7 +2352,6 @@ static int stm32_adc_remove(struct platform_device *pdev)
return 0;
}
-#if defined(CONFIG_PM_SLEEP)
static int stm32_adc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -2382,9 +2381,7 @@ static int stm32_adc_resume(struct device *dev)
return stm32_adc_buffer_postenable(indio_dev);
}
-#endif
-#if defined(CONFIG_PM)
static int stm32_adc_runtime_suspend(struct device *dev)
{
return stm32_adc_hw_stop(dev);
@@ -2394,12 +2391,11 @@ static int stm32_adc_runtime_resume(struct device *dev)
{
return stm32_adc_hw_start(dev);
}
-#endif
static const struct dev_pm_ops stm32_adc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume)
- SET_RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume)
+ RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume,
+ NULL)
};
static const struct stm32_adc_cfg stm32f4_adc_cfg = {
@@ -2453,7 +2449,7 @@ static struct platform_driver stm32_adc_driver = {
.driver = {
.name = "stm32-adc",
.of_match_table = stm32_adc_of_match,
- .pm = &stm32_adc_pm_ops,
+ .pm = pm_ptr(&stm32_adc_pm_ops),
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 1cfefb3b5e56..9704cf0b9753 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -1632,7 +1632,7 @@ static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev)
+static int stm32_dfsdm_adc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -1642,7 +1642,7 @@ static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev)
+static int stm32_dfsdm_adc_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
@@ -1665,14 +1665,15 @@ static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
- stm32_dfsdm_adc_suspend, stm32_dfsdm_adc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
+ stm32_dfsdm_adc_suspend,
+ stm32_dfsdm_adc_resume);
static struct platform_driver stm32_dfsdm_adc_driver = {
.driver = {
.name = "stm32-dfsdm-adc",
.of_match_table = stm32_dfsdm_adc_match,
- .pm = &stm32_dfsdm_adc_pm_ops,
+ .pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops),
},
.probe = stm32_dfsdm_adc_probe,
.remove = stm32_dfsdm_adc_remove,
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index a627af9a825e..a3d4de6ba4c2 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -381,7 +381,7 @@ static int stm32_dfsdm_core_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev)
+static int stm32_dfsdm_core_suspend(struct device *dev)
{
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
@@ -397,7 +397,7 @@ static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev)
return pinctrl_pm_select_sleep_state(dev);
}
-static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev)
+static int stm32_dfsdm_core_resume(struct device *dev)
{
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
@@ -414,7 +414,7 @@ static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev)
return pm_runtime_force_resume(dev);
}
-static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev)
+static int stm32_dfsdm_core_runtime_suspend(struct device *dev)
{
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
@@ -423,7 +423,7 @@ static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev)
+static int stm32_dfsdm_core_runtime_resume(struct device *dev)
{
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
@@ -431,11 +431,10 @@ static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend,
- stm32_dfsdm_core_resume)
- SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
- stm32_dfsdm_core_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, stm32_dfsdm_core_resume)
+ RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
+ stm32_dfsdm_core_runtime_resume,
+ NULL)
};
static struct platform_driver stm32_dfsdm_driver = {
@@ -444,7 +443,7 @@ static struct platform_driver stm32_dfsdm_driver = {
.driver = {
.name = "stm32-dfsdm",
.of_match_table = stm32_dfsdm_of_match,
- .pm = &stm32_dfsdm_core_pm_ops,
+ .pm = pm_ptr(&stm32_dfsdm_core_pm_ops),
},
};
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index ce3f5a3814f9..c9b5d9aec3dc 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -248,7 +248,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},
+ { ADC084S021_DRIVER_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(spi, adc084s021_id);
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
index e8fc4d01f30b..55b35570ad8b 100644
--- a/drivers/iio/adc/ti-tsc2046.c
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -82,6 +82,11 @@
#define TI_TSC2046_DATA_12BIT GENMASK(14, 3)
#define TI_TSC2046_MAX_CHAN 8
+#define TI_TSC2046_MIN_POLL_CNT 3
+#define TI_TSC2046_EXT_POLL_CNT 3
+#define TI_TSC2046_POLL_CNT \
+ (TI_TSC2046_MIN_POLL_CNT + TI_TSC2046_EXT_POLL_CNT)
+#define TI_TSC2046_INT_VREF 2500
/* Represents a HW sample */
struct tsc2046_adc_atom {
@@ -123,14 +128,23 @@ struct tsc2046_adc_ch_cfg {
unsigned int oversampling_ratio;
};
+enum tsc2046_state {
+ TSC2046_STATE_SHUTDOWN,
+ TSC2046_STATE_STANDBY,
+ TSC2046_STATE_POLL,
+ TSC2046_STATE_POLL_IRQ_DISABLE,
+ TSC2046_STATE_ENABLE_IRQ,
+};
+
struct tsc2046_adc_priv {
struct spi_device *spi;
const struct tsc2046_adc_dcfg *dcfg;
struct iio_trigger *trig;
struct hrtimer trig_timer;
- spinlock_t trig_lock;
- unsigned int trig_more_count;
+ enum tsc2046_state state;
+ int poll_cnt;
+ spinlock_t state_lock;
struct spi_transfer xfer;
struct spi_message msg;
@@ -153,9 +167,6 @@ struct tsc2046_adc_priv {
struct tsc2046_adc_atom *rx;
struct tsc2046_adc_atom *tx;
- struct tsc2046_adc_atom *rx_one;
- struct tsc2046_adc_atom *tx_one;
-
unsigned int count;
unsigned int groups;
u32 effective_speed_hz;
@@ -171,6 +182,8 @@ struct tsc2046_adc_priv {
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = "#name", \
.scan_index = index, \
.scan_type = { \
@@ -234,6 +247,14 @@ static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx,
else
pd = 0;
+ switch (ch_idx) {
+ case TI_TSC2046_ADDR_TEMP1:
+ case TI_TSC2046_ADDR_AUX:
+ case TI_TSC2046_ADDR_VBAT:
+ case TI_TSC2046_ADDR_TEMP0:
+ pd |= TI_TSC2046_SER | TI_TSC2046_PD1_VREF_ON;
+ }
+
return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd;
}
@@ -245,16 +266,50 @@ static u16 tsc2046_adc_get_value(struct tsc2046_adc_atom *buf)
static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
u32 *effective_speed_hz)
{
+ struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx];
+ struct tsc2046_adc_atom *rx_buf, *tx_buf;
+ unsigned int val, val_normalized = 0;
+ int ret, i, count_skip = 0, max_count;
struct spi_transfer xfer;
struct spi_message msg;
- int ret;
+ u8 cmd;
+
+ if (!effective_speed_hz) {
+ count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us);
+ max_count = count_skip + ch->oversampling_ratio;
+ } else {
+ max_count = 1;
+ }
+
+ if (sizeof(*tx_buf) * max_count > PAGE_SIZE)
+ return -ENOSPC;
+
+ tx_buf = kcalloc(max_count, sizeof(*tx_buf), GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ rx_buf = kcalloc(max_count, sizeof(*rx_buf), GFP_KERNEL);
+ if (!rx_buf) {
+ ret = -ENOMEM;
+ goto free_tx;
+ }
+
+ /*
+ * Do not enable automatic power down on working samples. Otherwise the
+ * plates will never be completely charged.
+ */
+ cmd = tsc2046_adc_get_cmd(priv, ch_idx, true);
+
+ for (i = 0; i < max_count - 1; i++)
+ tx_buf[i].cmd = cmd;
+
+ /* automatically power down on last sample */
+ tx_buf[i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
memset(&xfer, 0, sizeof(xfer));
- priv->tx_one->cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
- priv->tx_one->data = 0;
- xfer.tx_buf = priv->tx_one;
- xfer.rx_buf = priv->rx_one;
- xfer.len = sizeof(*priv->tx_one);
+ xfer.tx_buf = tx_buf;
+ xfer.rx_buf = rx_buf;
+ xfer.len = sizeof(*tx_buf) * max_count;
spi_message_init_with_transfers(&msg, &xfer, 1);
/*
@@ -265,13 +320,25 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
if (ret) {
dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n",
ERR_PTR(ret));
- return ret;
+ goto free_bufs;
}
if (effective_speed_hz)
*effective_speed_hz = xfer.effective_speed_hz;
- return tsc2046_adc_get_value(priv->rx_one);
+ for (i = 0; i < max_count - count_skip; i++) {
+ val = tsc2046_adc_get_value(&rx_buf[count_skip + i]);
+ val_normalized += val;
+ }
+
+ ret = DIV_ROUND_UP(val_normalized, max_count - count_skip);
+
+free_bufs:
+ kfree(rx_buf);
+free_tx:
+ kfree(tx_buf);
+
+ return ret;
}
static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv,
@@ -378,6 +445,37 @@ static irqreturn_t tsc2046_adc_trigger_handler(int irq, void *p)
return IRQ_HANDLED;
}
+static int tsc2046_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = tsc2046_adc_read_one(priv, chan->channel, NULL);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * Note: the TSC2046 has internal voltage divider on the VBAT
+ * line. This divider can be influenced by external divider.
+ * So, it is better to use external voltage-divider driver
+ * instead, which is calculating complete chain.
+ */
+ *val = TI_TSC2046_INT_VREF;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *active_scan_mask)
{
@@ -408,24 +506,67 @@ static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev,
}
static const struct iio_info tsc2046_adc_info = {
+ .read_raw = tsc2046_adc_read_raw,
.update_scan_mode = tsc2046_adc_update_scan_mode,
};
-static enum hrtimer_restart tsc2046_adc_trig_more(struct hrtimer *hrtimer)
+static enum hrtimer_restart tsc2046_adc_timer(struct hrtimer *hrtimer)
{
struct tsc2046_adc_priv *priv = container_of(hrtimer,
struct tsc2046_adc_priv,
trig_timer);
unsigned long flags;
- spin_lock_irqsave(&priv->trig_lock, flags);
-
- disable_irq_nosync(priv->spi->irq);
-
- priv->trig_more_count++;
- iio_trigger_poll(priv->trig);
+ /*
+ * This state machine should address following challenges :
+ * - the interrupt source is based on level shifter attached to the X
+ * channel of ADC. It will change the state every time we switch
+ * between channels. So, we need to disable IRQ if we do
+ * iio_trigger_poll().
+ * - we should do iio_trigger_poll() at some reduced sample rate
+ * - we should still trigger for some amount of time after last
+ * interrupt with enabled IRQ was processed.
+ */
- spin_unlock_irqrestore(&priv->trig_lock, flags);
+ spin_lock_irqsave(&priv->state_lock, flags);
+ switch (priv->state) {
+ case TSC2046_STATE_ENABLE_IRQ:
+ if (priv->poll_cnt < TI_TSC2046_POLL_CNT) {
+ priv->poll_cnt++;
+ hrtimer_start(&priv->trig_timer,
+ ns_to_ktime(priv->scan_interval_us *
+ NSEC_PER_USEC),
+ HRTIMER_MODE_REL_SOFT);
+
+ if (priv->poll_cnt >= TI_TSC2046_MIN_POLL_CNT) {
+ priv->state = TSC2046_STATE_POLL_IRQ_DISABLE;
+ enable_irq(priv->spi->irq);
+ } else {
+ priv->state = TSC2046_STATE_POLL;
+ }
+ } else {
+ priv->state = TSC2046_STATE_STANDBY;
+ enable_irq(priv->spi->irq);
+ }
+ break;
+ case TSC2046_STATE_POLL_IRQ_DISABLE:
+ disable_irq_nosync(priv->spi->irq);
+ fallthrough;
+ case TSC2046_STATE_POLL:
+ priv->state = TSC2046_STATE_ENABLE_IRQ;
+ /* iio_trigger_poll() starts hrtimer */
+ iio_trigger_poll(priv->trig);
+ break;
+ case TSC2046_STATE_SHUTDOWN:
+ break;
+ case TSC2046_STATE_STANDBY:
+ fallthrough;
+ default:
+ dev_warn(&priv->spi->dev, "Got unexpected state: %i\n",
+ priv->state);
+ break;
+ }
+ spin_unlock_irqrestore(&priv->state_lock, flags);
return HRTIMER_NORESTART;
}
@@ -434,16 +575,20 @@ static irqreturn_t tsc2046_adc_irq(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
-
- spin_lock(&priv->trig_lock);
+ unsigned long flags;
hrtimer_try_to_cancel(&priv->trig_timer);
- priv->trig_more_count = 0;
- disable_irq_nosync(priv->spi->irq);
- iio_trigger_poll(priv->trig);
+ spin_lock_irqsave(&priv->state_lock, flags);
+ if (priv->state != TSC2046_STATE_SHUTDOWN) {
+ priv->state = TSC2046_STATE_ENABLE_IRQ;
+ priv->poll_cnt = 0;
- spin_unlock(&priv->trig_lock);
+ /* iio_trigger_poll() starts hrtimer */
+ disable_irq_nosync(priv->spi->irq);
+ iio_trigger_poll(priv->trig);
+ }
+ spin_unlock_irqrestore(&priv->state_lock, flags);
return IRQ_HANDLED;
}
@@ -452,49 +597,42 @@ static void tsc2046_adc_reenable_trigger(struct iio_trigger *trig)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
- unsigned long flags;
- int delta;
+ ktime_t tim;
/*
* We can sample it as fast as we can, but usually we do not need so
* many samples. Reduce the sample rate for default (touchscreen) use
* case.
- * Currently we do not need a highly precise sample rate. It is enough
- * to have calculated numbers.
- */
- delta = priv->scan_interval_us - priv->time_per_scan_us;
- if (delta > 0)
- fsleep(delta);
-
- spin_lock_irqsave(&priv->trig_lock, flags);
-
- /*
- * We need to trigger at least one extra sample to detect state
- * difference on ADC side.
*/
- if (!priv->trig_more_count) {
- int timeout_ms = DIV_ROUND_UP(priv->scan_interval_us,
- USEC_PER_MSEC);
-
- hrtimer_start(&priv->trig_timer, ms_to_ktime(timeout_ms),
- HRTIMER_MODE_REL_SOFT);
- }
-
- enable_irq(priv->spi->irq);
-
- spin_unlock_irqrestore(&priv->trig_lock, flags);
+ tim = ns_to_ktime((priv->scan_interval_us - priv->time_per_scan_us) *
+ NSEC_PER_USEC);
+ hrtimer_start(&priv->trig_timer, tim, HRTIMER_MODE_REL_SOFT);
}
static int tsc2046_adc_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ unsigned long flags;
if (enable) {
- enable_irq(priv->spi->irq);
+ spin_lock_irqsave(&priv->state_lock, flags);
+ if (priv->state == TSC2046_STATE_SHUTDOWN) {
+ priv->state = TSC2046_STATE_STANDBY;
+ enable_irq(priv->spi->irq);
+ }
+ spin_unlock_irqrestore(&priv->state_lock, flags);
} else {
- disable_irq(priv->spi->irq);
- hrtimer_try_to_cancel(&priv->trig_timer);
+ spin_lock_irqsave(&priv->state_lock, flags);
+
+ if (priv->state == TSC2046_STATE_STANDBY ||
+ priv->state == TSC2046_STATE_POLL_IRQ_DISABLE)
+ disable_irq_nosync(priv->spi->irq);
+
+ priv->state = TSC2046_STATE_SHUTDOWN;
+ spin_unlock_irqrestore(&priv->state_lock, flags);
+
+ hrtimer_cancel(&priv->trig_timer);
}
return 0;
@@ -511,16 +649,6 @@ static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv)
size_t size;
int ret;
- priv->tx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->tx_one),
- GFP_KERNEL);
- if (!priv->tx_one)
- return -ENOMEM;
-
- priv->rx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->rx_one),
- GFP_KERNEL);
- if (!priv->rx_one)
- return -ENOMEM;
-
/*
* Make dummy read to set initial power state and get real SPI clock
* freq. It seems to be not important which channel is used for this
@@ -551,6 +679,12 @@ static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv)
for (ch_idx = 0; ch_idx < ARRAY_SIZE(priv->l); ch_idx++)
size += tsc2046_adc_group_set_layout(priv, ch_idx, ch_idx);
+ if (size > PAGE_SIZE) {
+ dev_err(&priv->spi->dev,
+ "Calculated scan buffer is too big. Try to reduce spi-max-frequency, settling-time-us or oversampling-ratio\n");
+ return -ENOSPC;
+ }
+
priv->tx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL);
if (!priv->tx)
return -ENOMEM;
@@ -668,10 +802,11 @@ static int tsc2046_adc_probe(struct spi_device *spi)
iio_trigger_set_drvdata(trig, indio_dev);
trig->ops = &tsc2046_adc_trigger_ops;
- spin_lock_init(&priv->trig_lock);
+ spin_lock_init(&priv->state_lock);
+ priv->state = TSC2046_STATE_SHUTDOWN;
hrtimer_init(&priv->trig_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
- priv->trig_timer.function = tsc2046_adc_trig_more;
+ priv->trig_timer.function = tsc2046_adc_timer;
ret = devm_iio_trigger_register(dev, trig);
if (ret) {
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 6ce40cc4568a..f8f8aea15612 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -231,13 +231,7 @@ static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
static struct twl4030_madc_data *twl4030_madc;
-struct twl4030_prescale_divider_ratios {
- s16 numerator;
- s16 denominator;
-};
-
-static const struct twl4030_prescale_divider_ratios
-twl4030_divider_ratios[16] = {
+static const struct s16_fract twl4030_divider_ratios[16] = {
{1, 1}, /* CHANNEL 0 No Prescaler */
{1, 1}, /* CHANNEL 1 No Prescaler */
{6, 10}, /* CHANNEL 2 */
@@ -256,7 +250,6 @@ twl4030_divider_ratios[16] = {
{5, 11}, /* CHANNEL 15 */
};
-
/* Conversion table from -3 to 55 degrees Celcius */
static int twl4030_therm_tbl[] = {
30800, 29500, 28300, 27100,
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index afdb59e0b526..f53e8558b560 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -911,6 +911,8 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, irq, NULL,
twl6030_gpadc_irq_handler,
IRQF_ONESHOT, "twl6030_gpadc", indio_dev);
+ if (ret)
+ return ret;
ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK);
if (ret < 0) {
@@ -944,7 +946,6 @@ static int twl6030_gpadc_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int twl6030_gpadc_suspend(struct device *pdev)
{
int ret;
@@ -968,17 +969,16 @@ static int twl6030_gpadc_resume(struct device *pdev)
return 0;
};
-#endif
-static SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend,
- twl6030_gpadc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend,
+ twl6030_gpadc_resume);
static struct platform_driver twl6030_gpadc_driver = {
.probe = twl6030_gpadc_probe,
.remove = twl6030_gpadc_remove,
.driver = {
.name = DRIVER_NAME,
- .pm = &twl6030_gpadc_pm_ops,
+ .pm = pm_sleep_ptr(&twl6030_gpadc_pm_ops),
.of_match_table = of_twl6030_match_tbl,
},
};
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index fd57fc43e8e5..c84293efc129 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -912,7 +912,6 @@ static int vf610_adc_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int vf610_adc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -952,9 +951,9 @@ disable_reg:
regulator_disable(info->vref);
return ret;
}
-#endif
-static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, vf610_adc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend,
+ vf610_adc_resume);
static struct platform_driver vf610_adc_driver = {
.probe = vf610_adc_probe,
@@ -962,7 +961,7 @@ static struct platform_driver vf610_adc_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = vf610_adc_match,
- .pm = &vf610_adc_pm_ops,
+ .pm = pm_sleep_ptr(&vf610_adc_pm_ops),
},
};
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
index 8343c5f74121..a55396c1f8b2 100644
--- a/drivers/iio/adc/xilinx-ams.c
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -12,6 +12,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/devm-helpers.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -91,8 +92,8 @@
#define AMS_CONF1_SEQ_MASK GENMASK(15, 12)
#define AMS_CONF1_SEQ_DEFAULT FIELD_PREP(AMS_CONF1_SEQ_MASK, 0)
-#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 1)
-#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 2)
+#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 2)
+#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 3)
#define AMS_REG_SEQ0_MASK GENMASK(15, 0)
#define AMS_REG_SEQ2_MASK GENMASK(21, 16)
@@ -530,14 +531,18 @@ static int ams_enable_single_channel(struct ams *ams, unsigned int offset)
return -EINVAL;
}
- /* set single channel, sequencer off mode */
+ /* put sysmon in a soft reset to change the sequence */
ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
- AMS_CONF1_SEQ_SINGLE_CHANNEL);
+ AMS_CONF1_SEQ_DEFAULT);
/* write the channel number */
ams_ps_update_reg(ams, AMS_REG_CONFIG0, AMS_CONF0_CHANNEL_NUM_MASK,
channel_num);
+ /* set single channel, sequencer off mode */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_SINGLE_CHANNEL);
+
return 0;
}
@@ -551,6 +556,8 @@ static int ams_read_vcc_reg(struct ams *ams, unsigned int offset, u32 *data)
if (ret)
return ret;
+ /* clear end-of-conversion flag, wait for next conversion to complete */
+ writel(expect, ams->base + AMS_ISR_1);
ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg, (reg & expect),
AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US);
if (ret)
@@ -1224,6 +1231,7 @@ static int ams_init_module(struct iio_dev *indio_dev,
/* add PS channels to iio device channels */
memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels));
+ num_channels = ARRAY_SIZE(ams_ps_channels);
} else if (fwnode_property_match_string(fwnode, "compatible",
"xlnx,zynqmp-ams-pl") == 0) {
ams->pl_base = fwnode_iomap(fwnode, 0);
@@ -1348,11 +1356,6 @@ static void ams_clk_disable_unprepare(void *data)
clk_disable_unprepare(data);
}
-static void ams_cancel_delayed_work(void *data)
-{
- cancel_delayed_work(data);
-}
-
static int ams_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
@@ -1389,9 +1392,8 @@ static int ams_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- INIT_DELAYED_WORK(&ams->ams_unmask_work, ams_unmask_worker);
- ret = devm_add_action_or_reset(&pdev->dev, ams_cancel_delayed_work,
- &ams->ams_unmask_work);
+ ret = devm_delayed_work_autocancel(&pdev->dev, &ams->ams_unmask_work,
+ ams_unmask_worker);
if (ret < 0)
return ret;
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index 774eb3044edd..7e511293d6d1 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -3,43 +3,152 @@
* IIO rescale driver
*
* Copyright (C) 2018 Axentia Technologies AB
+ * Copyright (C) 2022 Liam Beguin <liambeguin@gmail.com>
*
* Author: Peter Rosin <peda@axentia.se>
*/
#include <linux/err.h>
#include <linux/gcd.h>
-#include <linux/iio/consumer.h>
-#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/property.h>
-struct rescale;
+#include <linux/iio/afe/rescale.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
-struct rescale_cfg {
- enum iio_chan_type type;
- int (*props)(struct device *dev, struct rescale *rescale);
-};
+int rescale_process_scale(struct rescale *rescale, int scale_type,
+ int *val, int *val2)
+{
+ s64 tmp;
+ int _val, _val2;
+ s32 rem, rem2;
+ u32 mult;
+ u32 neg;
+
+ switch (scale_type) {
+ case IIO_VAL_INT:
+ *val *= rescale->numerator;
+ if (rescale->denominator == 1)
+ return scale_type;
+ *val2 = rescale->denominator;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_VAL_FRACTIONAL:
+ /*
+ * When the product of both scales doesn't overflow, avoid
+ * potential accuracy loss (for in kernel consumers) by
+ * keeping a fractional representation.
+ */
+ if (!check_mul_overflow(*val, rescale->numerator, &_val) &&
+ !check_mul_overflow(*val2, rescale->denominator, &_val2)) {
+ *val = _val;
+ *val2 = _val2;
+ return IIO_VAL_FRACTIONAL;
+ }
+ fallthrough;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = (s64)*val * 1000000000LL;
+ tmp = div_s64(tmp, rescale->denominator);
+ tmp *= rescale->numerator;
-struct rescale {
- const struct rescale_cfg *cfg;
- struct iio_channel *source;
- struct iio_chan_spec chan;
- struct iio_chan_spec_ext_info *ext_info;
- bool chan_processed;
- s32 numerator;
- s32 denominator;
-};
+ tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+ *val = tmp;
+
+ if (!rem)
+ return scale_type;
+
+ if (scale_type == IIO_VAL_FRACTIONAL)
+ tmp = *val2;
+ else
+ tmp = ULL(1) << *val2;
+
+ rem2 = *val % (int)tmp;
+ *val = *val / (int)tmp;
+
+ *val2 = rem / (int)tmp;
+ if (rem2)
+ *val2 += div_s64((s64)rem2 * 1000000000LL, tmp);
+
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_VAL_INT_PLUS_NANO:
+ case IIO_VAL_INT_PLUS_MICRO:
+ mult = scale_type == IIO_VAL_INT_PLUS_NANO ? 1000000000L : 1000000L;
+
+ /*
+ * For IIO_VAL_INT_PLUS_{MICRO,NANO} scale types if either *val
+ * OR *val2 is negative the schan scale is negative, i.e.
+ * *val = 1 and *val2 = -0.5 yields -1.5 not -0.5.
+ */
+ neg = *val < 0 || *val2 < 0;
+
+ tmp = (s64)abs(*val) * abs(rescale->numerator);
+ *val = div_s64_rem(tmp, abs(rescale->denominator), &rem);
+
+ tmp = (s64)rem * mult + (s64)abs(*val2) * abs(rescale->numerator);
+ tmp = div_s64(tmp, abs(rescale->denominator));
+
+ *val += div_s64_rem(tmp, mult, val2);
+
+ /*
+ * If only one of the rescaler elements or the schan scale is
+ * negative, the combined scale is negative.
+ */
+ if (neg ^ ((rescale->numerator < 0) ^ (rescale->denominator < 0))) {
+ if (*val)
+ *val = -*val;
+ else
+ *val2 = -*val2;
+ }
+
+ return scale_type;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int rescale_process_offset(struct rescale *rescale, int scale_type,
+ int scale, int scale2, int schan_off,
+ int *val, int *val2)
+{
+ s64 tmp, tmp2;
+
+ switch (scale_type) {
+ case IIO_VAL_FRACTIONAL:
+ tmp = (s64)rescale->offset * scale2;
+ *val = div_s64(tmp, scale) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT:
+ *val = div_s64(rescale->offset, scale) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = (s64)rescale->offset * (1 << scale2);
+ *val = div_s64(tmp, scale) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT_PLUS_NANO:
+ tmp = (s64)rescale->offset * 1000000000LL;
+ tmp2 = ((s64)scale * 1000000000LL) + scale2;
+ *val = div64_s64(tmp, tmp2) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT_PLUS_MICRO:
+ tmp = (s64)rescale->offset * 1000000LL;
+ tmp2 = ((s64)scale * 1000000LL) + scale2;
+ *val = div64_s64(tmp, tmp2) + schan_off;
+ return IIO_VAL_INT;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
static int rescale_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct rescale *rescale = iio_priv(indio_dev);
- unsigned long long tmp;
+ int scale, scale2;
+ int schan_off = 0;
int ret;
switch (mask) {
@@ -65,27 +174,48 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
} else {
ret = iio_read_channel_scale(rescale->source, val, val2);
}
- switch (ret) {
- case IIO_VAL_FRACTIONAL:
- *val *= rescale->numerator;
- *val2 *= rescale->denominator;
- return ret;
- case IIO_VAL_INT:
- *val *= rescale->numerator;
- if (rescale->denominator == 1)
- return ret;
- *val2 = rescale->denominator;
- return IIO_VAL_FRACTIONAL;
- case IIO_VAL_FRACTIONAL_LOG2:
- tmp = *val * 1000000000LL;
- do_div(tmp, rescale->denominator);
- tmp *= rescale->numerator;
- do_div(tmp, 1000000000LL);
- *val = tmp;
- return ret;
- default:
- return -EOPNOTSUPP;
+ return rescale_process_scale(rescale, ret, val, val2);
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * Processed channels are scaled 1-to-1 and source offset is
+ * already taken into account.
+ *
+ * In other cases, real world measurement are expressed as:
+ *
+ * schan_scale * (raw + schan_offset)
+ *
+ * Given that the rescaler parameters are applied recursively:
+ *
+ * rescaler_scale * (schan_scale * (raw + schan_offset) +
+ * rescaler_offset)
+ *
+ * Or,
+ *
+ * (rescaler_scale * schan_scale) * (raw +
+ * (schan_offset + rescaler_offset / schan_scale)
+ *
+ * Thus, reusing the original expression the parameters exposed
+ * to userspace are:
+ *
+ * scale = schan_scale * rescaler_scale
+ * offset = schan_offset + rescaler_offset / schan_scale
+ */
+ if (rescale->chan_processed) {
+ *val = rescale->offset;
+ return IIO_VAL_INT;
}
+
+ if (iio_channel_has_info(rescale->source->channel,
+ IIO_CHAN_INFO_OFFSET)) {
+ ret = iio_read_channel_offset(rescale->source,
+ &schan_off, NULL);
+ if (ret != IIO_VAL_INT)
+ return ret < 0 ? ret : -EOPNOTSUPP;
+ }
+
+ ret = iio_read_channel_scale(rescale->source, &scale, &scale2);
+ return rescale_process_offset(rescale, ret, scale, scale2,
+ schan_off, val, val2);
default:
return -EINVAL;
}
@@ -162,6 +292,9 @@ static int rescale_configure_channel(struct device *dev,
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE);
+ if (rescale->offset)
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET);
+
/*
* Using .read_avail() is fringe to begin with and makes no sense
* whatsoever for processed channels, so we make sure that this cannot
@@ -261,10 +394,78 @@ static int rescale_voltage_divider_props(struct device *dev,
return 0;
}
+static int rescale_temp_sense_rtd_props(struct device *dev,
+ struct rescale *rescale)
+{
+ u32 factor;
+ u32 alpha;
+ u32 iexc;
+ u32 tmp;
+ int ret;
+ u32 r0;
+
+ ret = device_property_read_u32(dev, "excitation-current-microamp",
+ &iexc);
+ if (ret) {
+ dev_err(dev, "failed to read excitation-current-microamp: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "alpha-ppm-per-celsius", &alpha);
+ if (ret) {
+ dev_err(dev, "failed to read alpha-ppm-per-celsius: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "r-naught-ohms", &r0);
+ if (ret) {
+ dev_err(dev, "failed to read r-naught-ohms: %d\n", ret);
+ return ret;
+ }
+
+ tmp = r0 * iexc * alpha / 1000000;
+ factor = gcd(tmp, 1000000);
+ rescale->numerator = 1000000 / factor;
+ rescale->denominator = tmp / factor;
+
+ rescale->offset = -1 * ((r0 * iexc) / 1000);
+
+ return 0;
+}
+
+static int rescale_temp_transducer_props(struct device *dev,
+ struct rescale *rescale)
+{
+ s32 offset = 0;
+ s32 sense = 1;
+ s32 alpha;
+ int ret;
+
+ device_property_read_u32(dev, "sense-offset-millicelsius", &offset);
+ device_property_read_u32(dev, "sense-resistor-ohms", &sense);
+ ret = device_property_read_u32(dev, "alpha-ppm-per-celsius", &alpha);
+ if (ret) {
+ dev_err(dev, "failed to read alpha-ppm-per-celsius: %d\n", ret);
+ return ret;
+ }
+
+ rescale->numerator = 1000000;
+ rescale->denominator = alpha * sense;
+
+ rescale->offset = div_s64((s64)offset * rescale->denominator,
+ rescale->numerator);
+
+ return 0;
+}
+
enum rescale_variant {
CURRENT_SENSE_AMPLIFIER,
CURRENT_SENSE_SHUNT,
VOLTAGE_DIVIDER,
+ TEMP_SENSE_RTD,
+ TEMP_TRANSDUCER,
};
static const struct rescale_cfg rescale_cfg[] = {
@@ -280,6 +481,14 @@ static const struct rescale_cfg rescale_cfg[] = {
.type = IIO_VOLTAGE,
.props = rescale_voltage_divider_props,
},
+ [TEMP_SENSE_RTD] = {
+ .type = IIO_TEMP,
+ .props = rescale_temp_sense_rtd_props,
+ },
+ [TEMP_TRANSDUCER] = {
+ .type = IIO_TEMP,
+ .props = rescale_temp_transducer_props,
+ },
};
static const struct of_device_id rescale_match[] = {
@@ -289,6 +498,10 @@ static const struct of_device_id rescale_match[] = {
.data = &rescale_cfg[CURRENT_SENSE_SHUNT], },
{ .compatible = "voltage-divider",
.data = &rescale_cfg[VOLTAGE_DIVIDER], },
+ { .compatible = "temperature-sense-rtd",
+ .data = &rescale_cfg[TEMP_SENSE_RTD], },
+ { .compatible = "temperature-transducer",
+ .data = &rescale_cfg[TEMP_TRANSDUCER], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rescale_match);
@@ -326,6 +539,7 @@ static int rescale_probe(struct platform_device *pdev)
rescale->cfg = of_device_get_match_data(dev);
rescale->numerator = 1;
rescale->denominator = 1;
+ rescale->offset = 0;
ret = rescale->cfg->props(dev, rescale);
if (ret)
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig
index 5eb1357a9c78..f217a2a1e958 100644
--- a/drivers/iio/amplifiers/Kconfig
+++ b/drivers/iio/amplifiers/Kconfig
@@ -23,6 +23,17 @@ config AD8366
To compile this driver as a module, choose M here: the
module will be called ad8366.
+config ADA4250
+ tristate "Analog Devices ADA4250 Instrumentation Amplifier"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADA4250
+ SPI Amplifier's support. The driver provides direct access via
+ sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ada4250.
+
config HMC425
tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers"
depends on GPIOLIB
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile
index cb551d82f56b..2126331129cf 100644
--- a/drivers/iio/amplifiers/Makefile
+++ b/drivers/iio/amplifiers/Makefile
@@ -5,4 +5,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD8366) += ad8366.o
+obj-$(CONFIG_ADA4250) += ada4250.o
obj-$(CONFIG_HMC425) += hmc425a.o
diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c
new file mode 100644
index 000000000000..4b32d350dc5d
--- /dev/null
+++ b/drivers/iio/amplifiers/ada4250.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADA4250 driver
+ *
+ * Copyright 2022 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <asm/unaligned.h>
+
+/* ADA4250 Register Map */
+#define ADA4250_REG_GAIN_MUX 0x00
+#define ADA4250_REG_REFBUF_EN 0x01
+#define ADA4250_REG_RESET 0x02
+#define ADA4250_REG_SNSR_CAL_VAL 0x04
+#define ADA4250_REG_SNSR_CAL_CNFG 0x05
+#define ADA4250_REG_DIE_REV 0x18
+#define ADA4250_REG_CHIP_ID 0x19
+
+/* ADA4250_REG_GAIN_MUX Map */
+#define ADA4250_GAIN_MUX_MSK GENMASK(2, 0)
+
+/* ADA4250_REG_REFBUF Map */
+#define ADA4250_REFBUF_MSK BIT(0)
+
+/* ADA4250_REG_RESET Map */
+#define ADA4250_RESET_MSK BIT(0)
+
+/* ADA4250_REG_SNSR_CAL_VAL Map */
+#define ADA4250_CAL_CFG_BIAS_MSK GENMASK(7, 0)
+
+/* ADA4250_REG_SNSR_CAL_CNFG Bit Definition */
+#define ADA4250_BIAS_SET_MSK GENMASK(3, 2)
+#define ADA4250_RANGE_SET_MSK GENMASK(1, 0)
+
+/* Miscellaneous definitions */
+#define ADA4250_CHIP_ID 0x4250
+#define ADA4250_RANGE1 0
+#define ADA4250_RANGE4 3
+
+/* ADA4250 current bias set */
+enum ada4250_current_bias {
+ ADA4250_BIAS_DISABLED,
+ ADA4250_BIAS_BANDGAP,
+ ADA4250_BIAS_AVDD,
+};
+
+struct ada4250_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator *reg;
+ /* Protect against concurrent accesses to the device and data content */
+ struct mutex lock;
+ u8 bias;
+ u8 gain;
+ int offset_uv;
+ bool refbuf_en;
+};
+
+/* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */
+static const int calibbias_table[] = {0, 1, 2};
+
+/* ADA4250 Gain (V/V) values: 1, 2, 4, 8, 16, 32, 64, 128 */
+static const int hwgain_table[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+static const struct regmap_config ada4250_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = BIT(7),
+ .max_register = 0x1A,
+};
+
+static int ada4250_set_offset_uv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int offset_uv)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+
+ int i, ret, x[8], max_vos, min_vos, voltage_v, vlsb = 0;
+ u8 offset_raw, range = ADA4250_RANGE1;
+ u32 lsb_coeff[6] = {1333, 2301, 4283, 8289, 16311, 31599};
+
+ if (st->bias == 0 || st->bias == 3)
+ return -EINVAL;
+
+ voltage_v = regulator_get_voltage(st->reg);
+ voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000);
+
+ if (st->bias == ADA4250_BIAS_AVDD)
+ x[0] = voltage_v;
+ else
+ x[0] = 5;
+
+ x[1] = 126 * (x[0] - 1);
+
+ for (i = 0; i < 6; i++)
+ x[i + 2] = DIV_ROUND_CLOSEST(x[1] * 1000, lsb_coeff[i]);
+
+ if (st->gain == 0)
+ return -EINVAL;
+
+ /*
+ * Compute Range and Voltage per LSB for the Sensor Offset Calibration
+ * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD):
+ * Range 1 Range 2
+ * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) |
+ * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 |
+ * 4 | X2*127 | X2=X1/1.3333 | X2*3*127 | X2*3 |
+ * 8 | X3*127 | X3=X1/2.301 | X3*3*127 | X3*3 |
+ * 16 | X4*127 | X4=X1/4.283 | X4*3*127 | X4*3 |
+ * 32 | X5*127 | X5=X1/8.289 | X5*3*127 | X5*3 |
+ * 64 | X6*127 | X6=X1/16.311 | X6*3*127 | X6*3 |
+ * 128 | X7*127 | X7=X1/31.599 | X7*3*127 | X7*3 |
+ */
+ for (i = ADA4250_RANGE1; i <= ADA4250_RANGE4; i++) {
+ max_vos = x[st->gain] * 127 * ((1 << (i + 1)) - 1);
+ min_vos = -1 * max_vos;
+ if (offset_uv > min_vos && offset_uv < max_vos) {
+ range = i;
+ vlsb = x[st->gain] * ((1 << (i + 1)) - 1);
+ break;
+ }
+ }
+
+ if (vlsb <= 0)
+ return -EINVAL;
+
+ offset_raw = DIV_ROUND_CLOSEST(abs(offset_uv), vlsb);
+
+ mutex_lock(&st->lock);
+ ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG,
+ ADA4250_RANGE_SET_MSK,
+ FIELD_PREP(ADA4250_RANGE_SET_MSK, range));
+ if (ret)
+ goto exit;
+
+ st->offset_uv = offset_raw * vlsb;
+
+ /*
+ * To set the offset calibration value, use bits [6:0] and bit 7 as the
+ * polarity bit (set to "0" for a negative offset and "1" for a positive
+ * offset).
+ */
+ if (offset_uv < 0) {
+ offset_raw |= BIT(7);
+ st->offset_uv *= (-1);
+ }
+
+ ret = regmap_write(st->regmap, ADA4250_REG_SNSR_CAL_VAL, offset_raw);
+
+exit:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ada4250_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = regmap_read(st->regmap, ADA4250_REG_GAIN_MUX, val);
+ if (ret)
+ return ret;
+
+ *val = BIT(*val);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = st->offset_uv;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, val);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(ADA4250_BIAS_SET_MSK, *val);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ *val2 = 1000000;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ada4250_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = regmap_write(st->regmap, ADA4250_REG_GAIN_MUX,
+ FIELD_PREP(ADA4250_GAIN_MUX_MSK, ilog2(val)));
+ if (ret)
+ return ret;
+
+ st->gain = ilog2(val);
+
+ return ret;
+ case IIO_CHAN_INFO_OFFSET:
+ return ada4250_set_offset_uv(indio_dev, chan, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG,
+ ADA4250_BIAS_SET_MSK,
+ FIELD_PREP(ADA4250_BIAS_SET_MSK, val));
+ if (ret)
+ return ret;
+
+ st->bias = val;
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ada4250_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_CALIBBIAS:
+ *vals = calibbias_table;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(calibbias_table);
+
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *vals = hwgain_table;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(hwgain_table);
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ada4250_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int write_val,
+ unsigned int *read_val)
+{
+ struct ada4250_state *st = iio_priv(indio_dev);
+
+ if (read_val)
+ return regmap_read(st->regmap, reg, read_val);
+ else
+ return regmap_write(st->regmap, reg, write_val);
+}
+
+static const struct iio_info ada4250_info = {
+ .read_raw = ada4250_read_raw,
+ .write_raw = ada4250_write_raw,
+ .read_avail = &ada4250_read_avail,
+ .debugfs_reg_access = &ada4250_reg_access,
+};
+
+static const struct iio_chan_spec ada4250_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ }
+};
+
+static void ada4250_reg_disable(void *data)
+{
+ regulator_disable(data);
+}
+
+static int ada4250_init(struct ada4250_state *st)
+{
+ int ret;
+ u16 chip_id;
+ u8 data[2] __aligned(8) = {};
+ struct spi_device *spi = st->spi;
+
+ st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable");
+
+ st->reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->reg))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->reg),
+ "failed to get the AVDD voltage\n");
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADA4250_REG_RESET,
+ FIELD_PREP(ADA4250_RESET_MSK, 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2);
+ if (ret)
+ return ret;
+
+ chip_id = get_unaligned_le16(data);
+
+ if (chip_id != ADA4250_CHIP_ID) {
+ dev_err(&spi->dev, "Invalid chip ID.\n");
+ return -EINVAL;
+ }
+
+ return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN,
+ FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en));
+}
+
+static int ada4250_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ struct ada4250_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_spi(spi, &ada4250_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ st = iio_priv(indio_dev);
+ st->regmap = regmap;
+ st->spi = spi;
+
+ indio_dev->info = &ada4250_info;
+ indio_dev->name = "ada4250";
+ indio_dev->channels = ada4250_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ada4250_channels);
+
+ mutex_init(&st->lock);
+
+ ret = ada4250_init(st);
+ if (ret) {
+ dev_err(&spi->dev, "ADA4250 init failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ada4250_id[] = {
+ { "ada4250", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ada4250_id);
+
+static const struct of_device_id ada4250_of_match[] = {
+ { .compatible = "adi,ada4250" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ada4250_of_match);
+
+static struct spi_driver ada4250_driver = {
+ .driver = {
+ .name = "ada4250",
+ .of_match_table = ada4250_of_match,
+ },
+ .probe = ada4250_probe,
+ .id_table = ada4250_id,
+};
+module_spi_driver(ada4250_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
+MODULE_DESCRIPTION("Analog Devices ADA4250");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c
index 16c0a77f6a1c..ce80e0c916f4 100644
--- a/drivers/iio/amplifiers/hmc425a.c
+++ b/drivers/iio/amplifiers/hmc425a.c
@@ -11,10 +11,10 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
@@ -192,7 +192,7 @@ static int hmc425a_probe(struct platform_device *pdev)
return -ENOMEM;
st = iio_priv(indio_dev);
- st->type = (uintptr_t)of_device_get_match_data(&pdev->dev);
+ st->type = (uintptr_t)device_get_match_data(&pdev->dev);
st->chip_info = &hmc425a_chip_info_tbl[st->type];
indio_dev->num_channels = st->chip_info->num_channels;
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index f8ce26a24c57..f744b62a636a 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -136,7 +136,7 @@ static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(buffer);
- return sprintf(buf, "%zu\n", dmaengine_buffer->align);
+ return sysfs_emit(buf, "%zu\n", dmaengine_buffer->align);
}
static IIO_DEVICE_ATTR(length_align_bytes, 0444,
diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c
index 87d9aabd20c7..fb58f599a80b 100644
--- a/drivers/iio/buffer/industrialio-hw-consumer.c
+++ b/drivers/iio/buffer/industrialio-hw-consumer.c
@@ -52,7 +52,6 @@ static const struct iio_buffer_access_funcs iio_hw_buf_access = {
static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
{
- size_t mask_size = BITS_TO_LONGS(indio_dev->masklength) * sizeof(long);
struct hw_consumer_buffer *buf;
list_for_each_entry(buf, &hwc->buffers, head) {
@@ -60,7 +59,8 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
return buf;
}
- buf = kzalloc(sizeof(*buf) + mask_size, GFP_KERNEL);
+ buf = kzalloc(struct_size(buf, scan_mask, BITS_TO_LONGS(indio_dev->masklength)),
+ GFP_KERNEL);
if (!buf)
return NULL;
diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c
index b1bacfe3c3ce..bbcf5a59c1f4 100644
--- a/drivers/iio/chemical/atlas-ezo-sensor.c
+++ b/drivers/iio/chemical/atlas-ezo-sensor.c
@@ -6,13 +6,15 @@
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/property.h>
#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
+
#include <linux/iio/iio.h>
#define ATLAS_EZO_DRV_NAME "atlas-ezo-sensor"
@@ -33,7 +35,7 @@ struct atlas_ezo_device {
struct atlas_ezo_data {
struct i2c_client *client;
- struct atlas_ezo_device *chip;
+ const struct atlas_ezo_device *chip;
/* lock to avoid multiple concurrent read calls */
struct mutex lock;
@@ -184,17 +186,17 @@ static const struct iio_info atlas_info = {
};
static const struct i2c_device_id atlas_ezo_id[] = {
- { "atlas-co2-ezo", ATLAS_CO2_EZO },
- { "atlas-o2-ezo", ATLAS_O2_EZO },
- { "atlas-hum-ezo", ATLAS_HUM_EZO },
+ { "atlas-co2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_CO2_EZO] },
+ { "atlas-o2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_O2_EZO] },
+ { "atlas-hum-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_HUM_EZO] },
{}
};
MODULE_DEVICE_TABLE(i2c, atlas_ezo_id);
static const struct of_device_id atlas_ezo_dt_ids[] = {
- { .compatible = "atlas,co2-ezo", .data = (void *)ATLAS_CO2_EZO, },
- { .compatible = "atlas,o2-ezo", .data = (void *)ATLAS_O2_EZO, },
- { .compatible = "atlas,hum-ezo", .data = (void *)ATLAS_HUM_EZO, },
+ { .compatible = "atlas,co2-ezo", .data = &atlas_ezo_devices[ATLAS_CO2_EZO], },
+ { .compatible = "atlas,o2-ezo", .data = &atlas_ezo_devices[ATLAS_O2_EZO], },
+ { .compatible = "atlas,hum-ezo", .data = &atlas_ezo_devices[ATLAS_HUM_EZO], },
{}
};
MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids);
@@ -202,20 +204,20 @@ MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids);
static int atlas_ezo_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ const struct atlas_ezo_device *chip;
struct atlas_ezo_data *data;
- struct atlas_ezo_device *chip;
- const struct of_device_id *of_id;
struct iio_dev *indio_dev;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
- of_id = of_match_device(atlas_ezo_dt_ids, &client->dev);
- if (!of_id)
- chip = &atlas_ezo_devices[id->driver_data];
+ if (dev_fwnode(&client->dev))
+ chip = device_get_match_data(&client->dev);
else
- chip = &atlas_ezo_devices[(unsigned long)of_id->data];
+ chip = (const struct atlas_ezo_device *)id->driver_data;
+ if (!chip)
+ return -EINVAL;
indio_dev->info = &atlas_info;
indio_dev->name = ATLAS_EZO_DRV_NAME;
diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c
index 04b44a327614..56dea9734c8d 100644
--- a/drivers/iio/chemical/atlas-sensor.c
+++ b/drivers/iio/chemical/atlas-sensor.c
@@ -589,11 +589,11 @@ static const struct iio_info atlas_info = {
};
static const struct i2c_device_id atlas_id[] = {
- { "atlas-ph-sm", ATLAS_PH_SM},
- { "atlas-ec-sm", ATLAS_EC_SM},
- { "atlas-orp-sm", ATLAS_ORP_SM},
- { "atlas-do-sm", ATLAS_DO_SM},
- { "atlas-rtd-sm", ATLAS_RTD_SM},
+ { "atlas-ph-sm", ATLAS_PH_SM },
+ { "atlas-ec-sm", ATLAS_EC_SM },
+ { "atlas-orp-sm", ATLAS_ORP_SM },
+ { "atlas-do-sm", ATLAS_DO_SM },
+ { "atlas-rtd-sm", ATLAS_RTD_SM },
{}
};
MODULE_DEVICE_TABLE(i2c, atlas_id);
@@ -737,7 +737,6 @@ static int atlas_remove(struct i2c_client *client)
return atlas_set_powermode(data, 0);
}
-#ifdef CONFIG_PM
static int atlas_runtime_suspend(struct device *dev)
{
struct atlas_data *data =
@@ -753,18 +752,16 @@ static int atlas_runtime_resume(struct device *dev)
return atlas_set_powermode(data, 1);
}
-#endif
static const struct dev_pm_ops atlas_pm_ops = {
- SET_RUNTIME_PM_OPS(atlas_runtime_suspend,
- atlas_runtime_resume, NULL)
+ RUNTIME_PM_OPS(atlas_runtime_suspend, atlas_runtime_resume, NULL)
};
static struct i2c_driver atlas_driver = {
.driver = {
.name = ATLAS_DRV_NAME,
.of_match_table = atlas_dt_ids,
- .pm = &atlas_pm_ops,
+ .pm = pm_ptr(&atlas_pm_ops),
},
.probe = atlas_probe,
.remove = atlas_remove,
diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c
index bf23cc7eb99e..16ff7a98c9f0 100644
--- a/drivers/iio/chemical/bme680_core.c
+++ b/drivers/iio/chemical/bme680_core.c
@@ -81,7 +81,7 @@ const struct regmap_config bme680_regmap_config = {
.volatile_table = &bme680_volatile_table,
.cache_type = REGCACHE_RBTREE,
};
-EXPORT_SYMBOL(bme680_regmap_config);
+EXPORT_SYMBOL_NS(bme680_regmap_config, IIO_BME680);
static const struct iio_chan_spec bme680_channels[] = {
{
@@ -957,7 +957,7 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(bme680_core_probe);
+EXPORT_SYMBOL_NS_GPL(bme680_core_probe, IIO_BME680);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 Driver");
diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c
index 74cf89c82c0a..20f2c20b6b02 100644
--- a/drivers/iio/chemical/bme680_i2c.c
+++ b/drivers/iio/chemical/bme680_i2c.c
@@ -60,3 +60,4 @@ module_i2c_driver(bme680_i2c_driver);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("BME680 I2C driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_BME680);
diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c
index cc579a7ac5ce..4404d42ae5ec 100644
--- a/drivers/iio/chemical/bme680_spi.c
+++ b/drivers/iio/chemical/bme680_spi.c
@@ -4,8 +4,8 @@
*
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@@ -163,3 +163,4 @@ module_spi_driver(bme680_spi_driver);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_BME680);
diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c
index 267bc3c05338..20d4e7584e92 100644
--- a/drivers/iio/chemical/scd4x.c
+++ b/drivers/iio/chemical/scd4x.c
@@ -423,7 +423,7 @@ static ssize_t calibration_auto_enable_show(struct device *dev,
val = (be16_to_cpu(bval) & SCD4X_READY_MASK) ? 1 : 0;
- return sprintf(buf, "%d\n", val);
+ return sysfs_emit(buf, "%d\n", val);
}
static ssize_t calibration_auto_enable_store(struct device *dev,
diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c
index d51314505115..abd67559e451 100644
--- a/drivers/iio/chemical/sps30.c
+++ b/drivers/iio/chemical/sps30.c
@@ -221,7 +221,7 @@ static ssize_t cleaning_period_show(struct device *dev,
if (ret)
return ret;
- return sprintf(buf, "%d\n", be32_to_cpu(val));
+ return sysfs_emit(buf, "%d\n", be32_to_cpu(val));
}
static ssize_t cleaning_period_store(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c
index 16ea697e945c..6633b35a94e6 100644
--- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c
+++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c
@@ -58,7 +58,7 @@ int ms_sensors_reset(void *cli, u8 cmd, unsigned int delay)
return 0;
}
-EXPORT_SYMBOL(ms_sensors_reset);
+EXPORT_SYMBOL_NS(ms_sensors_reset, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_read_prom_word() - PROM word read function
@@ -84,7 +84,7 @@ int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word)
return 0;
}
-EXPORT_SYMBOL(ms_sensors_read_prom_word);
+EXPORT_SYMBOL_NS(ms_sensors_read_prom_word, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_convert_and_read() - ADC conversion & read function
@@ -130,7 +130,7 @@ err:
dev_err(&client->dev, "Unable to make sensor adc conversion\n");
return ret;
}
-EXPORT_SYMBOL(ms_sensors_convert_and_read);
+EXPORT_SYMBOL_NS(ms_sensors_convert_and_read, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_crc_valid() - CRC check function
@@ -248,7 +248,7 @@ int ms_sensors_read_serial(struct i2c_client *client, u64 *sn)
return 0;
}
-EXPORT_SYMBOL(ms_sensors_read_serial);
+EXPORT_SYMBOL_NS(ms_sensors_read_serial, IIO_MEAS_SPEC_SENSORS);
static int ms_sensors_read_config_reg(struct i2c_client *client,
u8 *config_reg)
@@ -299,7 +299,7 @@ ssize_t ms_sensors_write_resolution(struct ms_ht_dev *dev_data,
MS_SENSORS_CONFIG_REG_WRITE,
config_reg);
}
-EXPORT_SYMBOL(ms_sensors_write_resolution);
+EXPORT_SYMBOL_NS(ms_sensors_write_resolution, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_show_battery_low() - Show device battery low indicator
@@ -324,9 +324,9 @@ ssize_t ms_sensors_show_battery_low(struct ms_ht_dev *dev_data,
if (ret)
return ret;
- return sprintf(buf, "%d\n", (config_reg & 0x40) >> 6);
+ return sysfs_emit(buf, "%d\n", (config_reg & 0x40) >> 6);
}
-EXPORT_SYMBOL(ms_sensors_show_battery_low);
+EXPORT_SYMBOL_NS(ms_sensors_show_battery_low, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_show_heater() - Show device heater
@@ -351,9 +351,9 @@ ssize_t ms_sensors_show_heater(struct ms_ht_dev *dev_data,
if (ret)
return ret;
- return sprintf(buf, "%d\n", (config_reg & 0x4) >> 2);
+ return sysfs_emit(buf, "%d\n", (config_reg & 0x4) >> 2);
}
-EXPORT_SYMBOL(ms_sensors_show_heater);
+EXPORT_SYMBOL_NS(ms_sensors_show_heater, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_write_heater() - Write device heater
@@ -401,7 +401,7 @@ ssize_t ms_sensors_write_heater(struct ms_ht_dev *dev_data,
return len;
}
-EXPORT_SYMBOL(ms_sensors_write_heater);
+EXPORT_SYMBOL_NS(ms_sensors_write_heater, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_ht_read_temperature() - Read temperature
@@ -442,7 +442,7 @@ int ms_sensors_ht_read_temperature(struct ms_ht_dev *dev_data,
return 0;
}
-EXPORT_SYMBOL(ms_sensors_ht_read_temperature);
+EXPORT_SYMBOL_NS(ms_sensors_ht_read_temperature, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_ht_read_humidity() - Read humidity
@@ -485,7 +485,7 @@ int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
return 0;
}
-EXPORT_SYMBOL(ms_sensors_ht_read_humidity);
+EXPORT_SYMBOL_NS(ms_sensors_ht_read_humidity, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_tp_crc4() - Calculate PROM CRC for
@@ -602,7 +602,7 @@ int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
return 0;
}
-EXPORT_SYMBOL(ms_sensors_tp_read_prom);
+EXPORT_SYMBOL_NS(ms_sensors_tp_read_prom, IIO_MEAS_SPEC_SENSORS);
/**
* ms_sensors_read_temp_and_pressure() - read temp and pressure
@@ -688,7 +688,7 @@ int ms_sensors_read_temp_and_pressure(struct ms_tp_dev *dev_data,
return 0;
}
-EXPORT_SYMBOL(ms_sensors_read_temp_and_pressure);
+EXPORT_SYMBOL_NS(ms_sensors_read_temp_and_pressure, IIO_MEAS_SPEC_SENSORS);
MODULE_DESCRIPTION("Measurement-Specialties common i2c driver");
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
index eafaf4529df5..e64d242145e0 100644
--- a/drivers/iio/common/ssp_sensors/ssp_dev.c
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -7,9 +7,10 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
+#include <linux/property.h>
+
#include "ssp.h"
#define SSP_WDT_TIME 10000
@@ -204,7 +205,7 @@ u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
{
return data->delay_buf[type];
}
-EXPORT_SYMBOL(ssp_get_sensor_delay);
+EXPORT_SYMBOL_NS(ssp_get_sensor_delay, IIO_SSP_SENSORS);
/**
* ssp_enable_sensor() - enables data acquisition for sensor
@@ -266,7 +267,7 @@ int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
derror:
return ret;
}
-EXPORT_SYMBOL(ssp_enable_sensor);
+EXPORT_SYMBOL_NS(ssp_enable_sensor, IIO_SSP_SENSORS);
/**
* ssp_change_delay() - changes data acquisition for sensor
@@ -297,7 +298,7 @@ int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
return 0;
}
-EXPORT_SYMBOL(ssp_change_delay);
+EXPORT_SYMBOL_NS(ssp_change_delay, IIO_SSP_SENSORS);
/**
* ssp_disable_sensor() - disables sensor
@@ -334,7 +335,7 @@ int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
return 0;
}
-EXPORT_SYMBOL(ssp_disable_sensor);
+EXPORT_SYMBOL_NS(ssp_disable_sensor, IIO_SSP_SENSORS);
static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
{
@@ -425,7 +426,6 @@ int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
msecs_to_jiffies(delay));
}
-#ifdef CONFIG_OF
static const struct of_device_id ssp_of_match[] = {
{
.compatible = "samsung,sensorhub-rinato",
@@ -441,8 +441,6 @@ MODULE_DEVICE_TABLE(of, ssp_of_match);
static struct ssp_data *ssp_parse_dt(struct device *dev)
{
struct ssp_data *data;
- struct device_node *node = dev->of_node;
- const struct of_device_id *match;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -461,22 +459,12 @@ static struct ssp_data *ssp_parse_dt(struct device *dev)
if (IS_ERR(data->mcu_reset_gpiod))
return NULL;
- match = of_match_node(ssp_of_match, node);
- if (!match)
- return NULL;
-
- data->sensorhub_info = match->data;
+ data->sensorhub_info = device_get_match_data(dev);
dev_set_drvdata(dev, data);
return data;
}
-#else
-static struct ssp_data *ssp_parse_dt(struct device *pdev)
-{
- return NULL;
-}
-#endif
/**
* ssp_register_consumer() - registers iio consumer in ssp framework
@@ -490,7 +478,7 @@ void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)
data->sensor_devs[type] = indio_dev;
}
-EXPORT_SYMBOL(ssp_register_consumer);
+EXPORT_SYMBOL_NS(ssp_register_consumer, IIO_SSP_SENSORS);
static int ssp_probe(struct spi_device *spi)
{
@@ -610,7 +598,6 @@ static void ssp_remove(struct spi_device *spi)
mfd_remove_devices(&spi->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int ssp_suspend(struct device *dev)
{
int ret;
@@ -659,18 +646,15 @@ static int ssp_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
-static const struct dev_pm_ops ssp_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(ssp_pm_ops, ssp_suspend, ssp_resume);
static struct spi_driver ssp_driver = {
.probe = ssp_probe,
.remove = ssp_remove,
.driver = {
- .pm = &ssp_pm_ops,
- .of_match_table = of_match_ptr(ssp_of_match),
+ .pm = pm_sleep_ptr(&ssp_pm_ops),
+ .of_match_table = ssp_of_match,
.name = "sensorhub"
},
};
diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c
index 5336db81ba0a..88b8b56bfa51 100644
--- a/drivers/iio/common/ssp_sensors/ssp_iio.c
+++ b/drivers/iio/common/ssp_sensors/ssp_iio.c
@@ -32,7 +32,7 @@ int ssp_common_buffer_postenable(struct iio_dev *indio_dev)
return ssp_enable_sensor(data, spd->type,
ssp_get_sensor_delay(data, spd->type));
}
-EXPORT_SYMBOL(ssp_common_buffer_postenable);
+EXPORT_SYMBOL_NS(ssp_common_buffer_postenable, IIO_SSP_SENSORS);
/**
* ssp_common_buffer_postdisable() - generic postdisable callback for ssp buffer
@@ -55,7 +55,7 @@ int ssp_common_buffer_postdisable(struct iio_dev *indio_dev)
return ret;
}
-EXPORT_SYMBOL(ssp_common_buffer_postdisable);
+EXPORT_SYMBOL_NS(ssp_common_buffer_postdisable, IIO_SSP_SENSORS);
/**
* ssp_common_process_data() - Common process data callback for ssp sensors
@@ -91,8 +91,9 @@ int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer,
calculated_time);
}
-EXPORT_SYMBOL(ssp_common_process_data);
+EXPORT_SYMBOL_NS(ssp_common_process_data, IIO_SSP_SENSORS);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub commons");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_SSP_SENSORS);
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
index 9364ec7a811f..eda8f347fda5 100644
--- a/drivers/iio/common/st_sensors/Kconfig
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -13,5 +13,3 @@ config IIO_ST_SENSORS_SPI
config IIO_ST_SENSORS_CORE
tristate
- select IIO_ST_SENSORS_I2C if I2C
- select IIO_ST_SENSORS_SPI if SPI_MASTER
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
index dccc471e79da..e2f108ca949c 100644
--- a/drivers/iio/common/st_sensors/st_sensors_buffer.c
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -8,7 +8,6 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
@@ -77,8 +76,4 @@ st_sensors_get_buffer_element_error:
return IRQ_HANDLED;
}
-EXPORT_SYMBOL(st_sensors_trigger_handler);
-
-MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_NS(st_sensors_trigger_handler, IIO_ST_SENSORS);
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index eb452d0c423c..fa9bcdf0d190 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -46,7 +46,7 @@ int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
+EXPORT_SYMBOL_NS(st_sensors_debugfs_reg_access, IIO_ST_SENSORS);
static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings,
unsigned int odr, struct st_sensor_odr_avl *odr_out)
@@ -106,7 +106,7 @@ int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
st_sensors_match_odr_error:
return err;
}
-EXPORT_SYMBOL(st_sensors_set_odr);
+EXPORT_SYMBOL_NS(st_sensors_set_odr, IIO_ST_SENSORS);
static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
unsigned int fs, int *index_fs_avl)
@@ -199,7 +199,7 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
set_enable_error:
return err;
}
-EXPORT_SYMBOL(st_sensors_set_enable);
+EXPORT_SYMBOL_NS(st_sensors_set_enable, IIO_ST_SENSORS);
int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
{
@@ -213,7 +213,7 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
axis_enable);
return err;
}
-EXPORT_SYMBOL(st_sensors_set_axis_enable);
+EXPORT_SYMBOL_NS(st_sensors_set_axis_enable, IIO_ST_SENSORS);
static void st_reg_disable(void *reg)
{
@@ -257,7 +257,7 @@ int st_sensors_power_enable(struct iio_dev *indio_dev)
return devm_add_action_or_reset(parent, st_reg_disable, pdata->vdd_io);
}
-EXPORT_SYMBOL(st_sensors_power_enable);
+EXPORT_SYMBOL_NS(st_sensors_power_enable, IIO_ST_SENSORS);
static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
struct st_sensors_platform_data *pdata)
@@ -352,7 +352,7 @@ void st_sensors_dev_name_probe(struct device *dev, char *name, int len)
/* The name from the match takes precedence if present */
strlcpy(name, match, len);
}
-EXPORT_SYMBOL(st_sensors_dev_name_probe);
+EXPORT_SYMBOL_NS(st_sensors_dev_name_probe, IIO_ST_SENSORS);
int st_sensors_init_sensor(struct iio_dev *indio_dev,
struct st_sensors_platform_data *pdata)
@@ -437,7 +437,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
return err;
}
-EXPORT_SYMBOL(st_sensors_init_sensor);
+EXPORT_SYMBOL_NS(st_sensors_init_sensor, IIO_ST_SENSORS);
int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
{
@@ -486,7 +486,7 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
st_accel_set_dataready_irq_error:
return err;
}
-EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+EXPORT_SYMBOL_NS(st_sensors_set_dataready_irq, IIO_ST_SENSORS);
int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
{
@@ -509,7 +509,7 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
st_sensors_match_scale_error:
return err;
}
-EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+EXPORT_SYMBOL_NS(st_sensors_set_fullscale_by_gain, IIO_ST_SENSORS);
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch, int *data)
@@ -572,7 +572,7 @@ out:
return err;
}
-EXPORT_SYMBOL(st_sensors_read_info_raw);
+EXPORT_SYMBOL_NS(st_sensors_read_info_raw, IIO_ST_SENSORS);
/*
* st_sensors_get_settings_index() - get index of the sensor settings for a
@@ -599,7 +599,7 @@ int st_sensors_get_settings_index(const char *name,
return -ENODEV;
}
-EXPORT_SYMBOL(st_sensors_get_settings_index);
+EXPORT_SYMBOL_NS(st_sensors_get_settings_index, IIO_ST_SENSORS);
/*
* st_sensors_verify_id() - verify sensor ID (WhoAmI) is matching with the
@@ -632,7 +632,7 @@ int st_sensors_verify_id(struct iio_dev *indio_dev)
return 0;
}
-EXPORT_SYMBOL(st_sensors_verify_id);
+EXPORT_SYMBOL_NS(st_sensors_verify_id, IIO_ST_SENSORS);
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -654,7 +654,7 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
return len;
}
-EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail);
+EXPORT_SYMBOL_NS(st_sensors_sysfs_sampling_frequency_avail, IIO_ST_SENSORS);
ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -678,7 +678,7 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
return len;
}
-EXPORT_SYMBOL(st_sensors_sysfs_scale_avail);
+EXPORT_SYMBOL_NS(st_sensors_sysfs_scale_avail, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
index 18bd3c3d99bc..ee95082c7410 100644
--- a/drivers/iio/common/st_sensors/st_sensors_i2c.c
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -61,7 +61,7 @@ int st_sensors_i2c_configure(struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL(st_sensors_i2c_configure);
+EXPORT_SYMBOL_NS(st_sensors_i2c_configure, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
index 7c60050e90dc..63e302c3fbaa 100644
--- a/drivers/iio/common/st_sensors/st_sensors_spi.c
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -113,7 +113,7 @@ int st_sensors_spi_configure(struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL(st_sensors_spi_configure);
+EXPORT_SYMBOL_NS(st_sensors_spi_configure, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
index 392d74449886..899b640c0a70 100644
--- a/drivers/iio/common/st_sensors/st_sensors_trigger.c
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -8,7 +8,6 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
@@ -228,7 +227,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL(st_sensors_allocate_trigger);
+EXPORT_SYMBOL_NS(st_sensors_allocate_trigger, IIO_ST_SENSORS);
int st_sensors_validate_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
@@ -240,8 +239,4 @@ int st_sensors_validate_device(struct iio_trigger *trig,
return 0;
}
-EXPORT_SYMBOL(st_sensors_validate_device);
-
-MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_NS(st_sensors_validate_device, IIO_ST_SENSORS);
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index bfcf7568de32..c0bf0d84197f 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,17 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config LTC2688
+ tristate "Analog Devices LTC2688 DAC spi driver"
+ depends on SPI
+ select REGMAP
+ help
+ Say yes here to build support for Analog Devices
+ LTC2688 converters (DAC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ltc2688.
+
config AD5686
tristate
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 01a50131572f..ec3e42713f00 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_DS4424) += ds4424.o
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
obj-$(CONFIG_LTC1660) += ltc1660.o
obj-$(CONFIG_LTC2632) += ltc2632.o
+obj-$(CONFIG_LTC2688) += ltc2688.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c
index 2fcc59728fd6..a424b7220b61 100644
--- a/drivers/iio/dac/ad5592r-base.c
+++ b/drivers/iio/dac/ad5592r-base.c
@@ -11,7 +11,6 @@
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -661,7 +660,7 @@ error_disable_reg:
return ret;
}
-EXPORT_SYMBOL_GPL(ad5592r_probe);
+EXPORT_SYMBOL_NS_GPL(ad5592r_probe, IIO_AD5592R);
void ad5592r_remove(struct device *dev)
{
@@ -675,7 +674,7 @@ void ad5592r_remove(struct device *dev)
if (st->reg)
regulator_disable(st->reg);
}
-EXPORT_SYMBOL_GPL(ad5592r_remove);
+EXPORT_SYMBOL_NS_GPL(ad5592r_remove, IIO_AD5592R);
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c
index 0f7abfa75bec..32d950bbb1ca 100644
--- a/drivers/iio/dac/ad5592r.c
+++ b/drivers/iio/dac/ad5592r.c
@@ -168,3 +168,4 @@ module_spi_driver(ad5592r_spi_driver);
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5592R);
diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c
index 64dd7a0bddf7..34e1319a9712 100644
--- a/drivers/iio/dac/ad5593r.c
+++ b/drivers/iio/dac/ad5593r.c
@@ -137,3 +137,4 @@ module_i2c_driver(ad5593r_driver);
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5593R multi-channel converters");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5592R);
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index d26fb29b6b04..8ba2ea70451a 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -135,3 +135,4 @@ module_spi_driver(ad5686_spi_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5686);
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index e592a995f404..f78dd3f33199 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -536,7 +536,7 @@ error_disable_reg:
regulator_disable(st->reg);
return ret;
}
-EXPORT_SYMBOL_GPL(ad5686_probe);
+EXPORT_SYMBOL_NS_GPL(ad5686_probe, IIO_AD5686);
void ad5686_remove(struct device *dev)
{
@@ -547,7 +547,7 @@ void ad5686_remove(struct device *dev)
if (!IS_ERR(st->reg))
regulator_disable(st->reg);
}
-EXPORT_SYMBOL_GPL(ad5686_remove);
+EXPORT_SYMBOL_NS_GPL(ad5686_remove, IIO_AD5686);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index 93f0e0e66c22..762503c1901b 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -125,3 +125,4 @@ module_i2c_driver(ad5686_i2c_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5686);
diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c
new file mode 100644
index 000000000000..e41861d29767
--- /dev/null
+++ b/drivers/iio/dac/ltc2688.c
@@ -0,0 +1,1071 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LTC2688 16 channel, 16 bit Voltage Output SoftSpan DAC driver
+ *
+ * Copyright 2022 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/limits.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define LTC2688_DAC_CHANNELS 16
+
+#define LTC2688_CMD_CH_CODE(x) (0x00 + (x))
+#define LTC2688_CMD_CH_SETTING(x) (0x10 + (x))
+#define LTC2688_CMD_CH_OFFSET(x) (0X20 + (x))
+#define LTC2688_CMD_CH_GAIN(x) (0x30 + (x))
+#define LTC2688_CMD_CH_CODE_UPDATE(x) (0x40 + (x))
+
+#define LTC2688_CMD_CONFIG 0x70
+#define LTC2688_CMD_POWERDOWN 0x71
+#define LTC2688_CMD_A_B_SELECT 0x72
+#define LTC2688_CMD_SW_TOGGLE 0x73
+#define LTC2688_CMD_TOGGLE_DITHER_EN 0x74
+#define LTC2688_CMD_THERMAL_STAT 0x77
+#define LTC2688_CMD_UPDATE_ALL 0x7C
+#define LTC2688_CMD_NOOP 0xFF
+
+#define LTC2688_READ_OPERATION 0x80
+
+/* Channel Settings */
+#define LTC2688_CH_SPAN_MSK GENMASK(2, 0)
+#define LTC2688_CH_OVERRANGE_MSK BIT(3)
+#define LTC2688_CH_TD_SEL_MSK GENMASK(5, 4)
+#define LTC2688_CH_TGP_MAX 3
+#define LTC2688_CH_DIT_PER_MSK GENMASK(8, 6)
+#define LTC2688_CH_DIT_PH_MSK GENMASK(10, 9)
+#define LTC2688_CH_MODE_MSK BIT(11)
+
+#define LTC2688_DITHER_RAW_MASK GENMASK(15, 2)
+#define LTC2688_CH_CALIBBIAS_MASK GENMASK(15, 2)
+#define LTC2688_DITHER_RAW_MAX_VAL (BIT(14) - 1)
+#define LTC2688_CH_CALIBBIAS_MAX_VAL (BIT(14) - 1)
+
+/* Configuration register */
+#define LTC2688_CONFIG_RST BIT(15)
+#define LTC2688_CONFIG_EXT_REF BIT(1)
+
+#define LTC2688_DITHER_FREQ_AVAIL_N 5
+
+enum {
+ LTC2688_SPAN_RANGE_0V_5V,
+ LTC2688_SPAN_RANGE_0V_10V,
+ LTC2688_SPAN_RANGE_M5V_5V,
+ LTC2688_SPAN_RANGE_M10V_10V,
+ LTC2688_SPAN_RANGE_M15V_15V,
+ LTC2688_SPAN_RANGE_MAX
+};
+
+enum {
+ LTC2688_MODE_DEFAULT,
+ LTC2688_MODE_DITHER_TOGGLE,
+};
+
+struct ltc2688_chan {
+ long dither_frequency[LTC2688_DITHER_FREQ_AVAIL_N];
+ bool overrange;
+ bool toggle_chan;
+ u8 mode;
+};
+
+struct ltc2688_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator_bulk_data regulators[2];
+ struct ltc2688_chan channels[LTC2688_DAC_CHANNELS];
+ struct iio_chan_spec *iio_chan;
+ /* lock to protect against multiple access to the device and shared data */
+ struct mutex lock;
+ int vref;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 tx_data[6] ____cacheline_aligned;
+ u8 rx_data[3];
+};
+
+static int ltc2688_spi_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct ltc2688_state *st = context;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx_data,
+ .bits_per_word = 8,
+ .len = reg_size + val_size,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx_data + 3,
+ .rx_buf = st->rx_data,
+ .bits_per_word = 8,
+ .len = reg_size + val_size,
+ },
+ };
+ int ret;
+
+ memcpy(st->tx_data, reg, reg_size);
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret)
+ return ret;
+
+ memcpy(val, &st->rx_data[1], val_size);
+
+ return 0;
+}
+
+static int ltc2688_spi_write(void *context, const void *data, size_t count)
+{
+ struct ltc2688_state *st = context;
+
+ return spi_write(st->spi, data, count);
+}
+
+static int ltc2688_span_get(const struct ltc2688_state *st, int c)
+{
+ int ret, reg, span;
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(c), &reg);
+ if (ret)
+ return ret;
+
+ span = FIELD_GET(LTC2688_CH_SPAN_MSK, reg);
+ /* sanity check to make sure we don't get any weird value from the HW */
+ if (span >= LTC2688_SPAN_RANGE_MAX)
+ return -EIO;
+
+ return span;
+}
+
+static const int ltc2688_span_helper[LTC2688_SPAN_RANGE_MAX][2] = {
+ {0, 5000}, {0, 10000}, {-5000, 5000}, {-10000, 10000}, {-15000, 15000},
+};
+
+static int ltc2688_scale_get(const struct ltc2688_state *st, int c, int *val)
+{
+ const struct ltc2688_chan *chan = &st->channels[c];
+ int span, fs;
+
+ span = ltc2688_span_get(st, c);
+ if (span < 0)
+ return span;
+
+ fs = ltc2688_span_helper[span][1] - ltc2688_span_helper[span][0];
+ if (chan->overrange)
+ fs = mult_frac(fs, 105, 100);
+
+ *val = DIV_ROUND_CLOSEST(fs * st->vref, 4096);
+
+ return 0;
+}
+
+static int ltc2688_offset_get(const struct ltc2688_state *st, int c, int *val)
+{
+ int span;
+
+ span = ltc2688_span_get(st, c);
+ if (span < 0)
+ return span;
+
+ if (ltc2688_span_helper[span][0] < 0)
+ *val = -32768;
+ else
+ *val = 0;
+
+ return 0;
+}
+
+enum {
+ LTC2688_INPUT_A,
+ LTC2688_INPUT_B,
+ LTC2688_INPUT_B_AVAIL,
+ LTC2688_DITHER_OFF,
+ LTC2688_DITHER_FREQ_AVAIL,
+};
+
+static int ltc2688_dac_code_write(struct ltc2688_state *st, u32 chan, u32 input,
+ u16 code)
+{
+ struct ltc2688_chan *c = &st->channels[chan];
+ int ret, reg;
+
+ /* 2 LSBs set to 0 if writing dither amplitude */
+ if (!c->toggle_chan && input == LTC2688_INPUT_B) {
+ if (code > LTC2688_DITHER_RAW_MAX_VAL)
+ return -EINVAL;
+
+ code = FIELD_PREP(LTC2688_DITHER_RAW_MASK, code);
+ }
+
+ mutex_lock(&st->lock);
+ /* select the correct input register to read from */
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan),
+ input << chan);
+ if (ret)
+ goto out_unlock;
+
+ /*
+ * If in dither/toggle mode the dac should be updated by an
+ * external signal (or sw toggle) and not here.
+ */
+ if (c->mode == LTC2688_MODE_DEFAULT)
+ reg = LTC2688_CMD_CH_CODE_UPDATE(chan);
+ else
+ reg = LTC2688_CMD_CH_CODE(chan);
+
+ ret = regmap_write(st->regmap, reg, code);
+out_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ltc2688_dac_code_read(struct ltc2688_state *st, u32 chan, u32 input,
+ u32 *code)
+{
+ struct ltc2688_chan *c = &st->channels[chan];
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan),
+ input << chan);
+ if (ret)
+ goto out_unlock;
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_CODE(chan), code);
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ if (!c->toggle_chan && input == LTC2688_INPUT_B)
+ *code = FIELD_GET(LTC2688_DITHER_RAW_MASK, *code);
+
+ return ret;
+}
+
+static const int ltc2688_raw_range[] = {0, 1, U16_MAX};
+
+static int ltc2688_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ *vals = ltc2688_raw_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2688_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long info)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ltc2688_dac_code_read(st, chan->channel, LTC2688_INPUT_A,
+ val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ ret = ltc2688_offset_get(st, chan->channel, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = ltc2688_scale_get(st, chan->channel, val);
+ if (ret)
+ return ret;
+
+ *val = 16;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(st->regmap,
+ LTC2688_CMD_CH_OFFSET(chan->channel), val);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(LTC2688_CH_CALIBBIAS_MASK, *val);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = regmap_read(st->regmap,
+ LTC2688_CMD_CH_GAIN(chan->channel), val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2688_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long info)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (val > U16_MAX || val < 0)
+ return -EINVAL;
+
+ return ltc2688_dac_code_write(st, chan->channel,
+ LTC2688_INPUT_A, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val > LTC2688_CH_CALIBBIAS_MAX_VAL)
+ return -EINVAL;
+
+ return regmap_write(st->regmap,
+ LTC2688_CMD_CH_OFFSET(chan->channel),
+ FIELD_PREP(LTC2688_CH_CALIBBIAS_MASK, val));
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return regmap_write(st->regmap,
+ LTC2688_CMD_CH_GAIN(chan->channel), val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ltc2688_dither_toggle_set(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ struct ltc2688_chan *c = &st->channels[chan->channel];
+ int ret;
+ bool en;
+
+ ret = kstrtobool(buf, &en);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_TOGGLE_DITHER_EN,
+ BIT(chan->channel), en << chan->channel);
+ if (ret)
+ goto out_unlock;
+
+ c->mode = en ? LTC2688_MODE_DITHER_TOGGLE : LTC2688_MODE_DEFAULT;
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret ?: len;
+}
+
+static ssize_t ltc2688_reg_bool_get(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(st->regmap, private, &val);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", !!(val & BIT(chan->channel)));
+}
+
+static ssize_t ltc2688_reg_bool_set(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ bool en;
+
+ ret = kstrtobool(buf, &en);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, private, BIT(chan->channel),
+ en << chan->channel);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t ltc2688_dither_freq_avail(const struct ltc2688_state *st,
+ const struct ltc2688_chan *chan,
+ char *buf)
+{
+ int sz = 0;
+ u32 f;
+
+ for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++)
+ sz += sysfs_emit_at(buf, sz, "%ld ", chan->dither_frequency[f]);
+
+ buf[sz - 1] = '\n';
+
+ return sz;
+}
+
+static ssize_t ltc2688_dither_freq_get(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ const struct ltc2688_chan *c = &st->channels[chan->channel];
+ u32 reg, freq;
+ int ret;
+
+ if (private == LTC2688_DITHER_FREQ_AVAIL)
+ return ltc2688_dither_freq_avail(st, c, buf);
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel),
+ &reg);
+ if (ret)
+ return ret;
+
+ freq = FIELD_GET(LTC2688_CH_DIT_PER_MSK, reg);
+ if (freq >= ARRAY_SIZE(c->dither_frequency))
+ return -EIO;
+
+ return sysfs_emit(buf, "%ld\n", c->dither_frequency[freq]);
+}
+
+static ssize_t ltc2688_dither_freq_set(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ const struct ltc2688_chan *c = &st->channels[chan->channel];
+ long val;
+ u32 freq;
+ int ret;
+
+ if (private == LTC2688_DITHER_FREQ_AVAIL)
+ return -EINVAL;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ for (freq = 0; freq < ARRAY_SIZE(c->dither_frequency); freq++) {
+ if (val == c->dither_frequency[freq])
+ break;
+ }
+
+ if (freq == ARRAY_SIZE(c->dither_frequency))
+ return -EINVAL;
+
+ ret = regmap_update_bits(st->regmap,
+ LTC2688_CMD_CH_SETTING(chan->channel),
+ LTC2688_CH_DIT_PER_MSK,
+ FIELD_PREP(LTC2688_CH_DIT_PER_MSK, freq));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t ltc2688_dac_input_read(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ if (private == LTC2688_INPUT_B_AVAIL)
+ return sysfs_emit(buf, "[%u %u %u]\n", ltc2688_raw_range[0],
+ ltc2688_raw_range[1],
+ ltc2688_raw_range[2] / 4);
+
+ if (private == LTC2688_DITHER_OFF)
+ return sysfs_emit(buf, "0\n");
+
+ ret = ltc2688_dac_code_read(st, chan->channel, private, &val);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t ltc2688_dac_input_write(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ u16 val;
+
+ if (private == LTC2688_INPUT_B_AVAIL || private == LTC2688_DITHER_OFF)
+ return -EINVAL;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = ltc2688_dac_code_write(st, chan->channel, private, val);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int ltc2688_get_dither_phase(struct iio_dev *dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ltc2688_state *st = iio_priv(dev);
+ int ret, regval;
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel),
+ &regval);
+ if (ret)
+ return ret;
+
+ return FIELD_GET(LTC2688_CH_DIT_PH_MSK, regval);
+}
+
+static int ltc2688_set_dither_phase(struct iio_dev *dev,
+ const struct iio_chan_spec *chan,
+ unsigned int phase)
+{
+ struct ltc2688_state *st = iio_priv(dev);
+
+ return regmap_update_bits(st->regmap,
+ LTC2688_CMD_CH_SETTING(chan->channel),
+ LTC2688_CH_DIT_PH_MSK,
+ FIELD_PREP(LTC2688_CH_DIT_PH_MSK, phase));
+}
+
+static int ltc2688_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static const char * const ltc2688_dither_phase[] = {
+ "0", "1.5708", "3.14159", "4.71239",
+};
+
+static const struct iio_enum ltc2688_dither_phase_enum = {
+ .items = ltc2688_dither_phase,
+ .num_items = ARRAY_SIZE(ltc2688_dither_phase),
+ .set = ltc2688_set_dither_phase,
+ .get = ltc2688_get_dither_phase,
+};
+
+#define LTC2688_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { \
+ .name = _name, \
+ .read = (_read), \
+ .write = (_write), \
+ .private = (_what), \
+ .shared = (_shared), \
+}
+
+/*
+ * For toggle mode we only expose the symbol attr (sw_toggle) in case a TGPx is
+ * not provided in dts.
+ */
+static const struct iio_chan_spec_ext_info ltc2688_toggle_sym_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN,
+ IIO_SEPARATE, ltc2688_reg_bool_get,
+ ltc2688_dither_toggle_set),
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ LTC2688_CHAN_EXT_INFO("symbol", LTC2688_CMD_SW_TOGGLE, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN,
+ IIO_SEPARATE, ltc2688_reg_bool_get,
+ ltc2688_dither_toggle_set),
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("dither_raw", LTC2688_INPUT_B, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("dither_raw_available", LTC2688_INPUT_B_AVAIL,
+ IIO_SEPARATE, ltc2688_dac_input_read,
+ ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("dither_offset", LTC2688_DITHER_OFF, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ /*
+ * Not IIO_ENUM because the available freq needs to be computed at
+ * probe. We could still use it, but it didn't felt much right.
+ */
+ LTC2688_CHAN_EXT_INFO("dither_frequency", 0, IIO_SEPARATE,
+ ltc2688_dither_freq_get, ltc2688_dither_freq_set),
+ LTC2688_CHAN_EXT_INFO("dither_frequency_available",
+ LTC2688_DITHER_FREQ_AVAIL, IIO_SEPARATE,
+ ltc2688_dither_freq_get, ltc2688_dither_freq_set),
+ IIO_ENUM("dither_phase", IIO_SEPARATE, &ltc2688_dither_phase_enum),
+ IIO_ENUM_AVAILABLE("dither_phase", IIO_SEPARATE,
+ &ltc2688_dither_phase_enum),
+ LTC2688_CHAN_EXT_INFO("dither_en", LTC2688_CMD_TOGGLE_DITHER_EN,
+ IIO_SEPARATE, ltc2688_reg_bool_get,
+ ltc2688_dither_toggle_set),
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+static const struct iio_chan_spec_ext_info ltc2688_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+#define LTC2688_CHANNEL(_chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
+ .ext_info = ltc2688_ext_info, \
+}
+
+static const struct iio_chan_spec ltc2688_channels[] = {
+ LTC2688_CHANNEL(0),
+ LTC2688_CHANNEL(1),
+ LTC2688_CHANNEL(2),
+ LTC2688_CHANNEL(3),
+ LTC2688_CHANNEL(4),
+ LTC2688_CHANNEL(5),
+ LTC2688_CHANNEL(6),
+ LTC2688_CHANNEL(7),
+ LTC2688_CHANNEL(8),
+ LTC2688_CHANNEL(9),
+ LTC2688_CHANNEL(10),
+ LTC2688_CHANNEL(11),
+ LTC2688_CHANNEL(12),
+ LTC2688_CHANNEL(13),
+ LTC2688_CHANNEL(14),
+ LTC2688_CHANNEL(15),
+};
+
+static void ltc2688_clk_disable(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static const int ltc2688_period[LTC2688_DITHER_FREQ_AVAIL_N] = {
+ 4, 8, 16, 32, 64,
+};
+
+static int ltc2688_tgp_clk_setup(struct ltc2688_state *st,
+ struct ltc2688_chan *chan,
+ struct fwnode_handle *node, int tgp)
+{
+ unsigned long rate;
+ struct clk *clk;
+ int ret, f;
+
+ clk = devm_get_clk_from_child(&st->spi->dev, to_of_node(node), NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&st->spi->dev, PTR_ERR(clk),
+ "failed to get tgp clk.\n");
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return dev_err_probe(&st->spi->dev, ret,
+ "failed to enable tgp clk.\n");
+
+ ret = devm_add_action_or_reset(&st->spi->dev, ltc2688_clk_disable, clk);
+ if (ret)
+ return ret;
+
+ if (chan->toggle_chan)
+ return 0;
+
+ /* calculate available dither frequencies */
+ rate = clk_get_rate(clk);
+ for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++)
+ chan->dither_frequency[f] = DIV_ROUND_CLOSEST(rate, ltc2688_period[f]);
+
+ return 0;
+}
+
+static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max)
+{
+ u32 span;
+
+ for (span = 0; span < ARRAY_SIZE(ltc2688_span_helper); span++) {
+ if (min == ltc2688_span_helper[span][0] &&
+ max == ltc2688_span_helper[span][1])
+ return span;
+ }
+
+ return -EINVAL;
+}
+
+static int ltc2688_channel_config(struct ltc2688_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct fwnode_handle *child;
+ u32 reg, clk_input, val, tmp[2];
+ int ret, span;
+
+ device_for_each_child_node(dev, child) {
+ struct ltc2688_chan *chan;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, ret,
+ "Failed to get reg property\n");
+ }
+
+ if (reg >= LTC2688_DAC_CHANNELS) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "reg bigger than: %d\n",
+ LTC2688_DAC_CHANNELS);
+ }
+
+ val = 0;
+ chan = &st->channels[reg];
+ if (fwnode_property_read_bool(child, "adi,toggle-mode")) {
+ chan->toggle_chan = true;
+ /* assume sw toggle ABI */
+ st->iio_chan[reg].ext_info = ltc2688_toggle_sym_ext_info;
+ /*
+ * Clear IIO_CHAN_INFO_RAW bit as toggle channels expose
+ * out_voltage_raw{0|1} files.
+ */
+ __clear_bit(IIO_CHAN_INFO_RAW,
+ &st->iio_chan[reg].info_mask_separate);
+ }
+
+ ret = fwnode_property_read_u32_array(child, "adi,output-range-microvolt",
+ tmp, ARRAY_SIZE(tmp));
+ if (!ret) {
+ span = ltc2688_span_lookup(st, (int)tmp[0] / 1000,
+ tmp[1] / 1000);
+ if (span < 0) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "output range not valid:[%d %d]\n",
+ tmp[0], tmp[1]);
+ }
+
+ val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span);
+ }
+
+ ret = fwnode_property_read_u32(child, "adi,toggle-dither-input",
+ &clk_input);
+ if (!ret) {
+ if (clk_input >= LTC2688_CH_TGP_MAX) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "toggle-dither-input inv value(%d)\n",
+ clk_input);
+ }
+
+ ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+
+ /*
+ * 0 means software toggle which is the default mode.
+ * Hence the +1.
+ */
+ val |= FIELD_PREP(LTC2688_CH_TD_SEL_MSK, clk_input + 1);
+
+ /*
+ * If a TGPx is given, we automatically assume a dither
+ * capable channel (unless toggle is already enabled).
+ * On top of this we just set here the dither bit in the
+ * channel settings. It won't have any effect until the
+ * global toggle/dither bit is enabled.
+ */
+ if (!chan->toggle_chan) {
+ val |= FIELD_PREP(LTC2688_CH_MODE_MSK, 1);
+ st->iio_chan[reg].ext_info = ltc2688_dither_ext_info;
+ } else {
+ /* wait, no sw toggle after all */
+ st->iio_chan[reg].ext_info = ltc2688_toggle_ext_info;
+ }
+ }
+
+ if (fwnode_property_read_bool(child, "adi,overrange")) {
+ chan->overrange = true;
+ val |= LTC2688_CH_OVERRANGE_MSK;
+ }
+
+ if (!val)
+ continue;
+
+ ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg),
+ val);
+ if (ret) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "failed to set chan settings\n");
+ }
+ }
+
+ return 0;
+}
+
+static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ /*
+ * If we have a reset pin, use that to reset the board, If not, use
+ * the reset bit.
+ */
+ gpio = devm_gpiod_get_optional(&st->spi->dev, "clr", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return dev_err_probe(&st->spi->dev, PTR_ERR(gpio),
+ "Failed to get reset gpio");
+ if (gpio) {
+ usleep_range(1000, 1200);
+ /* bring device out of reset */
+ gpiod_set_value_cansleep(gpio, 0);
+ } else {
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_CONFIG,
+ LTC2688_CONFIG_RST,
+ LTC2688_CONFIG_RST);
+ if (ret)
+ return ret;
+ }
+
+ usleep_range(10000, 12000);
+
+ /*
+ * Duplicate the default channel configuration as it can change during
+ * @ltc2688_channel_config()
+ */
+ st->iio_chan = devm_kmemdup(&st->spi->dev, ltc2688_channels,
+ sizeof(ltc2688_channels), GFP_KERNEL);
+ if (!st->iio_chan)
+ return -ENOMEM;
+
+ ret = ltc2688_channel_config(st);
+ if (ret)
+ return ret;
+
+ if (!vref)
+ return 0;
+
+ return regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG,
+ LTC2688_CONFIG_EXT_REF);
+}
+
+static void ltc2688_disable_regulators(void *data)
+{
+ struct ltc2688_state *st = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
+}
+
+static void ltc2688_disable_regulator(void *regulator)
+{
+ regulator_disable(regulator);
+}
+
+static bool ltc2688_reg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC2688_CMD_CH_CODE(0) ... LTC2688_CMD_CH_GAIN(15):
+ return true;
+ case LTC2688_CMD_CONFIG ... LTC2688_CMD_THERMAL_STAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ltc2688_reg_writable(struct device *dev, unsigned int reg)
+{
+ /*
+ * There's a jump from 0x76 to 0x78 in the write codes and the thermal
+ * status code is 0x77 (which is read only) so that we need to check
+ * that special condition.
+ */
+ if (reg <= LTC2688_CMD_UPDATE_ALL && reg != LTC2688_CMD_THERMAL_STAT)
+ return true;
+
+ return false;
+}
+
+static struct regmap_bus ltc2688_regmap_bus = {
+ .read = ltc2688_spi_read,
+ .write = ltc2688_spi_write,
+ .read_flag_mask = LTC2688_READ_OPERATION,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config ltc2688_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .readable_reg = ltc2688_reg_readable,
+ .writeable_reg = ltc2688_reg_writable,
+ /* ignoring the no op command */
+ .max_register = LTC2688_CMD_UPDATE_ALL,
+};
+
+static const struct iio_info ltc2688_info = {
+ .write_raw = ltc2688_write_raw,
+ .read_raw = ltc2688_read_raw,
+ .read_avail = ltc2688_read_avail,
+ .debugfs_reg_access = ltc2688_reg_access,
+};
+
+static int ltc2688_probe(struct spi_device *spi)
+{
+ struct ltc2688_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *vref_reg;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ /* Just write this once. No need to do it in every regmap read. */
+ st->tx_data[3] = LTC2688_CMD_NOOP;
+ mutex_init(&st->lock);
+
+ st->regmap = devm_regmap_init(dev, &ltc2688_regmap_bus, st,
+ &ltc2688_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to init regmap");
+
+ st->regulators[0].supply = "vcc";
+ st->regulators[1].supply = "iovcc";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators),
+ st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(dev, ltc2688_disable_regulators, st);
+ if (ret)
+ return ret;
+
+ vref_reg = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(vref_reg)) {
+ if (PTR_ERR(vref_reg) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(vref_reg),
+ "Failed to get vref regulator");
+
+ vref_reg = NULL;
+ /* internal reference */
+ st->vref = 4096;
+ } else {
+ ret = regulator_enable(vref_reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable vref regulators\n");
+
+ ret = devm_add_action_or_reset(dev, ltc2688_disable_regulator,
+ vref_reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(vref_reg);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get vref\n");
+
+ st->vref = ret / 1000;
+ }
+
+ ret = ltc2688_setup(st, vref_reg);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ltc2688";
+ indio_dev->info = &ltc2688_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->iio_chan;
+ indio_dev->num_channels = ARRAY_SIZE(ltc2688_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ltc2688_of_id[] = {
+ { .compatible = "adi,ltc2688" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ltc2688_of_id);
+
+static const struct spi_device_id ltc2688_id[] = {
+ { "ltc2688" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ltc2688_id);
+
+static struct spi_driver ltc2688_driver = {
+ .driver = {
+ .name = "ltc2688",
+ .of_match_table = ltc2688_of_id,
+ },
+ .probe = ltc2688_probe,
+ .id_table = ltc2688_id,
+};
+module_spi_driver(ltc2688_driver);
+
+MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2688 DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
index 225b1a374dc1..22b02f50fe41 100644
--- a/drivers/iio/dac/m62332.c
+++ b/drivers/iio/dac/m62332.c
@@ -25,9 +25,7 @@ struct m62332_data {
struct regulator *vcc;
struct mutex mutex;
u8 raw[M62332_CHANNELS];
-#ifdef CONFIG_PM_SLEEP
u8 save[M62332_CHANNELS];
-#endif
};
static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel)
@@ -124,7 +122,6 @@ static int m62332_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-#ifdef CONFIG_PM_SLEEP
static int m62332_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -156,11 +153,7 @@ static int m62332_resume(struct device *dev)
return m62332_set_value(indio_dev, data->save[1], 1);
}
-static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
-#define M62332_PM_OPS (&m62332_pm_ops)
-#else
-#define M62332_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
static const struct iio_info m62332_info = {
.read_raw = m62332_read_raw,
@@ -246,7 +239,7 @@ MODULE_DEVICE_TABLE(i2c, m62332_id);
static struct i2c_driver m62332_driver = {
.driver = {
.name = "m62332",
- .pm = M62332_PM_OPS,
+ .pm = pm_sleep_ptr(&m62332_pm_ops),
},
.probe = m62332_probe,
.remove = m62332_remove,
diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c
index bd7a3b20e645..83bf184e3adc 100644
--- a/drivers/iio/dac/stm32-dac-core.c
+++ b/drivers/iio/dac/stm32-dac-core.c
@@ -195,7 +195,7 @@ static int stm32_dac_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_dac_core_resume(struct device *dev)
+static int stm32_dac_core_resume(struct device *dev)
{
struct stm32_dac_common *common = dev_get_drvdata(dev);
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
@@ -213,23 +213,23 @@ static int __maybe_unused stm32_dac_core_resume(struct device *dev)
return pm_runtime_force_resume(dev);
}
-static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev)
+static int stm32_dac_core_runtime_suspend(struct device *dev)
{
stm32_dac_core_hw_stop(dev);
return 0;
}
-static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev)
+static int stm32_dac_core_runtime_resume(struct device *dev)
{
return stm32_dac_core_hw_start(dev);
}
static const struct dev_pm_ops stm32_dac_core_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume)
- SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend,
- stm32_dac_core_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume)
+ RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend,
+ stm32_dac_core_runtime_resume,
+ NULL)
};
static const struct stm32_dac_cfg stm32h7_dac_cfg = {
@@ -253,7 +253,7 @@ static struct platform_driver stm32_dac_driver = {
.driver = {
.name = "stm32-dac-core",
.of_match_table = stm32_dac_of_match,
- .pm = &stm32_dac_core_pm_ops,
+ .pm = pm_ptr(&stm32_dac_core_pm_ops),
},
};
module_platform_driver(stm32_dac_driver);
diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
index cd71cc4553a7..b20192a071cb 100644
--- a/drivers/iio/dac/stm32-dac.c
+++ b/drivers/iio/dac/stm32-dac.c
@@ -372,7 +372,7 @@ static int stm32_dac_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_dac_suspend(struct device *dev)
+static int stm32_dac_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
int channel = indio_dev->channels[0].channel;
@@ -386,9 +386,8 @@ static int __maybe_unused stm32_dac_suspend(struct device *dev)
return pm_runtime_force_suspend(dev);
}
-static const struct dev_pm_ops stm32_dac_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dac_pm_ops, stm32_dac_suspend,
+ pm_runtime_force_resume);
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32-dac", },
@@ -402,7 +401,7 @@ static struct platform_driver stm32_dac_driver = {
.driver = {
.name = "stm32-dac",
.of_match_table = stm32_dac_of_match,
- .pm = &stm32_dac_pm_ops,
+ .pm = pm_sleep_ptr(&stm32_dac_pm_ops),
},
};
module_platform_driver(stm32_dac_driver);
diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c
index 636b4009f763..92429c0d2685 100644
--- a/drivers/iio/dac/vf610_dac.c
+++ b/drivers/iio/dac/vf610_dac.c
@@ -242,7 +242,6 @@ static int vf610_dac_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int vf610_dac_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -268,9 +267,9 @@ static int vf610_dac_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend,
+ vf610_dac_resume);
static struct platform_driver vf610_dac_driver = {
.probe = vf610_dac_probe,
@@ -278,7 +277,7 @@ static struct platform_driver vf610_dac_driver = {
.driver = {
.name = "vf610-dac",
.of_match_table = vf610_dac_match,
- .pm = &vf610_dac_pm_ops,
+ .pm = pm_sleep_ptr(&vf610_dac_pm_ops),
},
};
module_platform_driver(vf610_dac_driver);
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index b44036f843af..f3702f36436c 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -60,6 +60,26 @@ config ADMV1013
To compile this driver as a module, choose M here: the
module will be called admv1013.
+config ADMV1014
+ tristate "Analog Devices ADMV1014 Microwave Downconverter"
+ depends on SPI && COMMON_CLK && 64BIT
+ help
+ Say yes here to build support for Analog Devices ADMV1014
+ 24 GHz to 44 GHz, Wideband, Microwave Downconverter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called admv1014.
+
+config ADMV4420
+ tristate "Analog Devices ADMV4420 K Band Downconverter"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices K Band
+ Downconverter with integrated Fractional-N PLL and VCO.
+
+ To compile this driver as a module, choose M here: the
+ module will be called admv4420.
+
config ADRF6780
tristate "Analog Devices ADRF6780 Microwave Upconverter"
depends on SPI
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index ae6899856c99..48add732f1d3 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -8,4 +8,6 @@ obj-$(CONFIG_AD9523) += ad9523.o
obj-$(CONFIG_ADF4350) += adf4350.o
obj-$(CONFIG_ADF4371) += adf4371.o
obj-$(CONFIG_ADMV1013) += admv1013.o
+obj-$(CONFIG_ADMV1014) += admv1014.o
+obj-$(CONFIG_ADMV4420) += admv4420.o
obj-$(CONFIG_ADRF6780) += adrf6780.o
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
index bdb0bc3b12dd..a0f92c336fc4 100644
--- a/drivers/iio/frequency/ad9523.c
+++ b/drivers/iio/frequency/ad9523.c
@@ -551,7 +551,7 @@ static ssize_t ad9523_show(struct device *dev,
mutex_lock(&st->lock);
ret = ad9523_read(indio_dev, AD9523_READBACK_0);
if (ret >= 0) {
- ret = sprintf(buf, "%d\n", !!(ret & (1 <<
+ ret = sysfs_emit(buf, "%d\n", !!(ret & (1 <<
(u32)this_attr->address)));
}
mutex_unlock(&st->lock);
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index f3521330f6fb..be1218d86291 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -7,17 +7,18 @@
#include <linux/device.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
-#include <linux/module.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <asm/div64.h>
#include <linux/clk.h>
-#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -381,10 +382,8 @@ static const struct iio_info adf4350_info = {
.debugfs_reg_access = &adf4350_reg_access,
};
-#ifdef CONFIG_OF
static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
{
- struct device_node *np = dev->of_node;
struct adf4350_platform_data *pdata;
unsigned int tmp;
@@ -392,101 +391,83 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
if (!pdata)
return NULL;
- snprintf(&pdata->name[0], SPI_NAME_SIZE - 1, "%pOFn", np);
+ snprintf(pdata->name, sizeof(pdata->name), "%pfw", dev_fwnode(dev));
tmp = 10000;
- of_property_read_u32(np, "adi,channel-spacing", &tmp);
+ device_property_read_u32(dev, "adi,channel-spacing", &tmp);
pdata->channel_spacing = tmp;
tmp = 0;
- of_property_read_u32(np, "adi,power-up-frequency", &tmp);
+ device_property_read_u32(dev, "adi,power-up-frequency", &tmp);
pdata->power_up_frequency = tmp;
tmp = 0;
- of_property_read_u32(np, "adi,reference-div-factor", &tmp);
+ device_property_read_u32(dev, "adi,reference-div-factor", &tmp);
pdata->ref_div_factor = tmp;
- pdata->ref_doubler_en = of_property_read_bool(np,
- "adi,reference-doubler-enable");
- pdata->ref_div2_en = of_property_read_bool(np,
- "adi,reference-div2-enable");
+ pdata->ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable");
+ pdata->ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable");
/* r2_user_settings */
- pdata->r2_user_settings = of_property_read_bool(np,
- "adi,phase-detector-polarity-positive-enable") ?
- ADF4350_REG2_PD_POLARITY_POS : 0;
- pdata->r2_user_settings |= of_property_read_bool(np,
- "adi,lock-detect-precision-6ns-enable") ?
- ADF4350_REG2_LDP_6ns : 0;
- pdata->r2_user_settings |= of_property_read_bool(np,
- "adi,lock-detect-function-integer-n-enable") ?
- ADF4350_REG2_LDF_INT_N : 0;
+ pdata->r2_user_settings = 0;
+ if (device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable"))
+ pdata->r2_user_settings |= ADF4350_REG2_PD_POLARITY_POS;
+ if (device_property_read_bool(dev, "adi,lock-detect-precision-6ns-enable"))
+ pdata->r2_user_settings |= ADF4350_REG2_LDP_6ns;
+ if (device_property_read_bool(dev, "adi,lock-detect-function-integer-n-enable"))
+ pdata->r2_user_settings |= ADF4350_REG2_LDF_INT_N;
tmp = 2500;
- of_property_read_u32(np, "adi,charge-pump-current", &tmp);
+ device_property_read_u32(dev, "adi,charge-pump-current", &tmp);
pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp);
tmp = 0;
- of_property_read_u32(np, "adi,muxout-select", &tmp);
+ device_property_read_u32(dev, "adi,muxout-select", &tmp);
pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp);
- pdata->r2_user_settings |= of_property_read_bool(np,
- "adi,low-spur-mode-enable") ?
- ADF4350_REG2_NOISE_MODE(0x3) : 0;
+ if (device_property_read_bool(dev, "adi,low-spur-mode-enable"))
+ pdata->r2_user_settings |= ADF4350_REG2_NOISE_MODE(0x3);
/* r3_user_settings */
- pdata->r3_user_settings = of_property_read_bool(np,
- "adi,cycle-slip-reduction-enable") ?
- ADF4350_REG3_12BIT_CSR_EN : 0;
- pdata->r3_user_settings |= of_property_read_bool(np,
- "adi,charge-cancellation-enable") ?
- ADF4351_REG3_CHARGE_CANCELLATION_EN : 0;
-
- pdata->r3_user_settings |= of_property_read_bool(np,
- "adi,anti-backlash-3ns-enable") ?
- ADF4351_REG3_ANTI_BACKLASH_3ns_EN : 0;
- pdata->r3_user_settings |= of_property_read_bool(np,
- "adi,band-select-clock-mode-high-enable") ?
- ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH : 0;
+ pdata->r3_user_settings = 0;
+ if (device_property_read_bool(dev, "adi,cycle-slip-reduction-enable"))
+ pdata->r3_user_settings |= ADF4350_REG3_12BIT_CSR_EN;
+ if (device_property_read_bool(dev, "adi,charge-cancellation-enable"))
+ pdata->r3_user_settings |= ADF4351_REG3_CHARGE_CANCELLATION_EN;
+ if (device_property_read_bool(dev, "adi,anti-backlash-3ns-enable"))
+ pdata->r3_user_settings |= ADF4351_REG3_ANTI_BACKLASH_3ns_EN;
+ if (device_property_read_bool(dev, "adi,band-select-clock-mode-high-enable"))
+ pdata->r3_user_settings |= ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH;
tmp = 0;
- of_property_read_u32(np, "adi,12bit-clk-divider", &tmp);
+ device_property_read_u32(dev, "adi,12bit-clk-divider", &tmp);
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp);
tmp = 0;
- of_property_read_u32(np, "adi,clk-divider-mode", &tmp);
+ device_property_read_u32(dev, "adi,clk-divider-mode", &tmp);
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp);
/* r4_user_settings */
- pdata->r4_user_settings = of_property_read_bool(np,
- "adi,aux-output-enable") ?
- ADF4350_REG4_AUX_OUTPUT_EN : 0;
- pdata->r4_user_settings |= of_property_read_bool(np,
- "adi,aux-output-fundamental-enable") ?
- ADF4350_REG4_AUX_OUTPUT_FUND : 0;
- pdata->r4_user_settings |= of_property_read_bool(np,
- "adi,mute-till-lock-enable") ?
- ADF4350_REG4_MUTE_TILL_LOCK_EN : 0;
+ pdata->r4_user_settings = 0;
+ if (device_property_read_bool(dev, "adi,aux-output-enable"))
+ pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_EN;
+ if (device_property_read_bool(dev, "adi,aux-output-fundamental-enable"))
+ pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_FUND;
+ if (device_property_read_bool(dev, "adi,mute-till-lock-enable"))
+ pdata->r4_user_settings |= ADF4350_REG4_MUTE_TILL_LOCK_EN;
tmp = 0;
- of_property_read_u32(np, "adi,output-power", &tmp);
+ device_property_read_u32(dev, "adi,output-power", &tmp);
pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp);
tmp = 0;
- of_property_read_u32(np, "adi,aux-output-power", &tmp);
+ device_property_read_u32(dev, "adi,aux-output-power", &tmp);
pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp);
return pdata;
}
-#else
-static
-struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
-{
- return NULL;
-}
-#endif
static int adf4350_probe(struct spi_device *spi)
{
@@ -496,7 +477,7 @@ static int adf4350_probe(struct spi_device *spi)
struct clk *clk = NULL;
int ret;
- if (spi->dev.of_node) {
+ if (dev_fwnode(&spi->dev)) {
pdata = adf4350_parse_dt(&spi->dev);
if (pdata == NULL)
return -EINVAL;
@@ -623,7 +604,7 @@ MODULE_DEVICE_TABLE(spi, adf4350_id);
static struct spi_driver adf4350_driver = {
.driver = {
.name = "adf4350",
- .of_match_table = of_match_ptr(adf4350_of_match),
+ .of_match_table = adf4350_of_match,
},
.probe = adf4350_probe,
.remove = adf4350_remove,
diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c
index 3f3c478e9baa..b0e1f6571afb 100644
--- a/drivers/iio/frequency/admv1013.c
+++ b/drivers/iio/frequency/admv1013.c
@@ -630,7 +630,7 @@ static int admv1013_probe(struct spi_device *spi)
}
static const struct spi_device_id admv1013_id[] = {
- { "admv1013", 0},
+ { "admv1013", 0 },
{}
};
MODULE_DEVICE_TABLE(spi, admv1013_id);
diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c
new file mode 100644
index 000000000000..a7994f8e6b9b
--- /dev/null
+++ b/drivers/iio/frequency/admv1014.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADMV1014 driver
+ *
+ * Copyright 2022 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <asm/unaligned.h>
+
+/* ADMV1014 Register Map */
+#define ADMV1014_REG_SPI_CONTROL 0x00
+#define ADMV1014_REG_ALARM 0x01
+#define ADMV1014_REG_ALARM_MASKS 0x02
+#define ADMV1014_REG_ENABLE 0x03
+#define ADMV1014_REG_QUAD 0x04
+#define ADMV1014_REG_LO_AMP_PHASE_ADJUST1 0x05
+#define ADMV1014_REG_MIXER 0x07
+#define ADMV1014_REG_IF_AMP 0x08
+#define ADMV1014_REG_IF_AMP_BB_AMP 0x09
+#define ADMV1014_REG_BB_AMP_AGC 0x0A
+#define ADMV1014_REG_VVA_TEMP_COMP 0x0B
+
+/* ADMV1014_REG_SPI_CONTROL Map */
+#define ADMV1014_PARITY_EN_MSK BIT(15)
+#define ADMV1014_SPI_SOFT_RESET_MSK BIT(14)
+#define ADMV1014_CHIP_ID_MSK GENMASK(11, 4)
+#define ADMV1014_CHIP_ID 0x9
+#define ADMV1014_REVISION_ID_MSK GENMASK(3, 0)
+
+/* ADMV1014_REG_ALARM Map */
+#define ADMV1014_PARITY_ERROR_MSK BIT(15)
+#define ADMV1014_TOO_FEW_ERRORS_MSK BIT(14)
+#define ADMV1014_TOO_MANY_ERRORS_MSK BIT(13)
+#define ADMV1014_ADDRESS_RANGE_ERROR_MSK BIT(12)
+
+/* ADMV1014_REG_ENABLE Map */
+#define ADMV1014_IBIAS_PD_MSK BIT(14)
+#define ADMV1014_P1DB_COMPENSATION_MSK GENMASK(13, 12)
+#define ADMV1014_IF_AMP_PD_MSK BIT(11)
+#define ADMV1014_QUAD_BG_PD_MSK BIT(9)
+#define ADMV1014_BB_AMP_PD_MSK BIT(8)
+#define ADMV1014_QUAD_IBIAS_PD_MSK BIT(7)
+#define ADMV1014_DET_EN_MSK BIT(6)
+#define ADMV1014_BG_PD_MSK BIT(5)
+
+/* ADMV1014_REG_QUAD Map */
+#define ADMV1014_QUAD_SE_MODE_MSK GENMASK(9, 6)
+#define ADMV1014_QUAD_FILTERS_MSK GENMASK(3, 0)
+
+/* ADMV1014_REG_LO_AMP_PHASE_ADJUST1 Map */
+#define ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK GENMASK(15, 9)
+#define ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK GENMASK(8, 2)
+
+/* ADMV1014_REG_MIXER Map */
+#define ADMV1014_MIXER_VGATE_MSK GENMASK(15, 9)
+#define ADMV1014_DET_PROG_MSK GENMASK(6, 0)
+
+/* ADMV1014_REG_IF_AMP Map */
+#define ADMV1014_IF_AMP_COARSE_GAIN_I_MSK GENMASK(11, 8)
+#define ADMV1014_IF_AMP_FINE_GAIN_Q_MSK GENMASK(7, 4)
+#define ADMV1014_IF_AMP_FINE_GAIN_I_MSK GENMASK(3, 0)
+
+/* ADMV1014_REG_IF_AMP_BB_AMP Map */
+#define ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK GENMASK(15, 12)
+#define ADMV1014_BB_AMP_OFFSET_Q_MSK GENMASK(9, 5)
+#define ADMV1014_BB_AMP_OFFSET_I_MSK GENMASK(4, 0)
+
+/* ADMV1014_REG_BB_AMP_AGC Map */
+#define ADMV1014_BB_AMP_REF_GEN_MSK GENMASK(6, 3)
+#define ADMV1014_BB_AMP_GAIN_CTRL_MSK GENMASK(2, 1)
+#define ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK BIT(0)
+
+/* ADMV1014_REG_VVA_TEMP_COMP Map */
+#define ADMV1014_VVA_TEMP_COMP_MSK GENMASK(15, 0)
+
+/* ADMV1014 Miscellaneous Defines */
+#define ADMV1014_READ BIT(7)
+#define ADMV1014_REG_ADDR_READ_MSK GENMASK(6, 1)
+#define ADMV1014_REG_ADDR_WRITE_MSK GENMASK(22, 17)
+#define ADMV1014_REG_DATA_MSK GENMASK(16, 1)
+#define ADMV1014_NUM_REGULATORS 9
+
+enum {
+ ADMV1014_IQ_MODE,
+ ADMV1014_IF_MODE,
+};
+
+enum {
+ ADMV1014_SE_MODE_POS = 6,
+ ADMV1014_SE_MODE_NEG = 9,
+ ADMV1014_SE_MODE_DIFF = 12,
+};
+
+enum {
+ ADMV1014_CALIBSCALE_COARSE,
+ ADMV1014_CALIBSCALE_FINE,
+};
+
+static const int detector_table[] = {0, 1, 2, 4, 8, 16, 32, 64};
+
+static const char * const input_mode_names[] = { "iq", "if" };
+
+static const char * const quad_se_mode_names[] = { "se-pos", "se-neg", "diff" };
+
+struct admv1014_state {
+ struct spi_device *spi;
+ struct clk *clkin;
+ struct notifier_block nb;
+ /* Protect against concurrent accesses to the device and to data*/
+ struct mutex lock;
+ struct regulator_bulk_data regulators[ADMV1014_NUM_REGULATORS];
+ unsigned int input_mode;
+ unsigned int quad_se_mode;
+ unsigned int p1db_comp;
+ bool det_en;
+ u8 data[3] ____cacheline_aligned;
+};
+
+static const int mixer_vgate_table[] = {106, 107, 108, 110, 111, 112, 113, 114,
+ 117, 118, 119, 120, 122, 123, 44, 45};
+
+static int __admv1014_spi_read(struct admv1014_state *st, unsigned int reg,
+ unsigned int *val)
+{
+ struct spi_transfer t = {};
+ int ret;
+
+ st->data[0] = ADMV1014_READ | FIELD_PREP(ADMV1014_REG_ADDR_READ_MSK, reg);
+ st->data[1] = 0;
+ st->data[2] = 0;
+
+ t.rx_buf = &st->data[0];
+ t.tx_buf = &st->data[0];
+ t.len = sizeof(st->data);
+
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(ADMV1014_REG_DATA_MSK, get_unaligned_be24(&st->data[0]));
+
+ return ret;
+}
+
+static int admv1014_spi_read(struct admv1014_state *st, unsigned int reg,
+ unsigned int *val)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = __admv1014_spi_read(st, reg, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int __admv1014_spi_write(struct admv1014_state *st,
+ unsigned int reg,
+ unsigned int val)
+{
+ put_unaligned_be24(FIELD_PREP(ADMV1014_REG_DATA_MSK, val) |
+ FIELD_PREP(ADMV1014_REG_ADDR_WRITE_MSK, reg), &st->data[0]);
+
+ return spi_write(st->spi, &st->data[0], 3);
+}
+
+static int admv1014_spi_write(struct admv1014_state *st, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = __admv1014_spi_write(st, reg, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int __admv1014_spi_update_bits(struct admv1014_state *st, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int data, temp;
+ int ret;
+
+ ret = __admv1014_spi_read(st, reg, &data);
+ if (ret)
+ return ret;
+
+ temp = (data & ~mask) | (val & mask);
+
+ return __admv1014_spi_write(st, reg, temp);
+}
+
+static int admv1014_spi_update_bits(struct admv1014_state *st, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = __admv1014_spi_update_bits(st, reg, mask, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int admv1014_update_quad_filters(struct admv1014_state *st)
+{
+ unsigned int filt_raw;
+ u64 rate = clk_get_rate(st->clkin);
+
+ if (rate >= (5400 * HZ_PER_MHZ) && rate <= (7000 * HZ_PER_MHZ))
+ filt_raw = 15;
+ else if (rate > (7000 * HZ_PER_MHZ) && rate <= (8000 * HZ_PER_MHZ))
+ filt_raw = 10;
+ else if (rate > (8000 * HZ_PER_MHZ) && rate <= (9200 * HZ_PER_MHZ))
+ filt_raw = 5;
+ else
+ filt_raw = 0;
+
+ return __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD,
+ ADMV1014_QUAD_FILTERS_MSK,
+ FIELD_PREP(ADMV1014_QUAD_FILTERS_MSK, filt_raw));
+}
+
+static int admv1014_update_vcm_settings(struct admv1014_state *st)
+{
+ unsigned int i, vcm_mv, vcm_comp, bb_sw_hl_cm;
+ int ret;
+
+ vcm_mv = regulator_get_voltage(st->regulators[0].consumer) / 1000;
+ for (i = 0; i < ARRAY_SIZE(mixer_vgate_table); i++) {
+ vcm_comp = 1050 + mult_frac(i, 450, 8);
+ if (vcm_mv != vcm_comp)
+ continue;
+
+ ret = __admv1014_spi_update_bits(st, ADMV1014_REG_MIXER,
+ ADMV1014_MIXER_VGATE_MSK,
+ FIELD_PREP(ADMV1014_MIXER_VGATE_MSK,
+ mixer_vgate_table[i]));
+ if (ret)
+ return ret;
+
+ bb_sw_hl_cm = ~(i / 8);
+ bb_sw_hl_cm = FIELD_PREP(ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK, bb_sw_hl_cm);
+
+ return __admv1014_spi_update_bits(st, ADMV1014_REG_BB_AMP_AGC,
+ ADMV1014_BB_AMP_REF_GEN_MSK |
+ ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK,
+ FIELD_PREP(ADMV1014_BB_AMP_REF_GEN_MSK, i) |
+ bb_sw_hl_cm);
+ }
+
+ return -EINVAL;
+}
+
+static int admv1014_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct admv1014_state *st = iio_priv(indio_dev);
+ unsigned int data;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_OFFSET:
+ ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP_BB_AMP, &data);
+ if (ret)
+ return ret;
+
+ if (chan->channel2 == IIO_MOD_I)
+ *val = FIELD_GET(ADMV1014_BB_AMP_OFFSET_I_MSK, data);
+ else
+ *val = FIELD_GET(ADMV1014_BB_AMP_OFFSET_Q_MSK, data);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PHASE:
+ ret = admv1014_spi_read(st, ADMV1014_REG_LO_AMP_PHASE_ADJUST1, &data);
+ if (ret)
+ return ret;
+
+ if (chan->channel2 == IIO_MOD_I)
+ *val = FIELD_GET(ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK, data);
+ else
+ *val = FIELD_GET(ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK, data);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = admv1014_spi_read(st, ADMV1014_REG_MIXER, &data);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(ADMV1014_DET_PROG_MSK, data);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = admv1014_spi_read(st, ADMV1014_REG_BB_AMP_AGC, &data);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(ADMV1014_BB_AMP_GAIN_CTRL_MSK, data);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int admv1014_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ int data;
+ unsigned int msk;
+ struct admv1014_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->channel2 == IIO_MOD_I) {
+ msk = ADMV1014_BB_AMP_OFFSET_I_MSK;
+ data = FIELD_PREP(ADMV1014_BB_AMP_OFFSET_I_MSK, val);
+ } else {
+ msk = ADMV1014_BB_AMP_OFFSET_Q_MSK;
+ data = FIELD_PREP(ADMV1014_BB_AMP_OFFSET_Q_MSK, val);
+ }
+
+ return admv1014_spi_update_bits(st, ADMV1014_REG_IF_AMP_BB_AMP, msk, data);
+ case IIO_CHAN_INFO_PHASE:
+ if (chan->channel2 == IIO_MOD_I) {
+ msk = ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK;
+ data = FIELD_PREP(ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK, val);
+ } else {
+ msk = ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK;
+ data = FIELD_PREP(ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK, val);
+ }
+
+ return admv1014_spi_update_bits(st, ADMV1014_REG_LO_AMP_PHASE_ADJUST1, msk, data);
+ case IIO_CHAN_INFO_SCALE:
+ return admv1014_spi_update_bits(st, ADMV1014_REG_MIXER,
+ ADMV1014_DET_PROG_MSK,
+ FIELD_PREP(ADMV1014_DET_PROG_MSK, val));
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return admv1014_spi_update_bits(st, ADMV1014_REG_BB_AMP_AGC,
+ ADMV1014_BB_AMP_GAIN_CTRL_MSK,
+ FIELD_PREP(ADMV1014_BB_AMP_GAIN_CTRL_MSK, val));
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t admv1014_read(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct admv1014_state *st = iio_priv(indio_dev);
+ unsigned int data;
+ int ret;
+
+ switch (private) {
+ case ADMV1014_CALIBSCALE_COARSE:
+ if (chan->channel2 == IIO_MOD_I) {
+ ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP, &data);
+ if (ret)
+ return ret;
+
+ data = FIELD_GET(ADMV1014_IF_AMP_COARSE_GAIN_I_MSK, data);
+ } else {
+ ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP_BB_AMP, &data);
+ if (ret)
+ return ret;
+
+ data = FIELD_GET(ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK, data);
+ }
+ break;
+ case ADMV1014_CALIBSCALE_FINE:
+ ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP, &data);
+ if (ret)
+ return ret;
+
+ if (chan->channel2 == IIO_MOD_I)
+ data = FIELD_GET(ADMV1014_IF_AMP_FINE_GAIN_I_MSK, data);
+ else
+ data = FIELD_GET(ADMV1014_IF_AMP_FINE_GAIN_Q_MSK, data);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", data);
+}
+
+static ssize_t admv1014_write(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct admv1014_state *st = iio_priv(indio_dev);
+ unsigned int data, addr, msk;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &data);
+ if (ret)
+ return ret;
+
+ switch (private) {
+ case ADMV1014_CALIBSCALE_COARSE:
+ if (chan->channel2 == IIO_MOD_I) {
+ addr = ADMV1014_REG_IF_AMP;
+ msk = ADMV1014_IF_AMP_COARSE_GAIN_I_MSK;
+ data = FIELD_PREP(ADMV1014_IF_AMP_COARSE_GAIN_I_MSK, data);
+ } else {
+ addr = ADMV1014_REG_IF_AMP_BB_AMP;
+ msk = ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK;
+ data = FIELD_PREP(ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK, data);
+ }
+ break;
+ case ADMV1014_CALIBSCALE_FINE:
+ addr = ADMV1014_REG_IF_AMP;
+
+ if (chan->channel2 == IIO_MOD_I) {
+ msk = ADMV1014_IF_AMP_FINE_GAIN_I_MSK;
+ data = FIELD_PREP(ADMV1014_IF_AMP_FINE_GAIN_I_MSK, data);
+ } else {
+ msk = ADMV1014_IF_AMP_FINE_GAIN_Q_MSK;
+ data = FIELD_PREP(ADMV1014_IF_AMP_FINE_GAIN_Q_MSK, data);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = admv1014_spi_update_bits(st, addr, msk, data);
+
+ return ret ? ret : len;
+}
+
+static int admv1014_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = detector_table;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(detector_table);
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int admv1014_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int write_val,
+ unsigned int *read_val)
+{
+ struct admv1014_state *st = iio_priv(indio_dev);
+
+ if (read_val)
+ return admv1014_spi_read(st, reg, read_val);
+ else
+ return admv1014_spi_write(st, reg, write_val);
+}
+
+static const struct iio_info admv1014_info = {
+ .read_raw = admv1014_read_raw,
+ .write_raw = admv1014_write_raw,
+ .read_avail = &admv1014_read_avail,
+ .debugfs_reg_access = &admv1014_reg_access,
+};
+
+static const char * const admv1014_reg_name[] = {
+ "vcm", "vcc-if-bb", "vcc-vga", "vcc-vva", "vcc-lna-3p3",
+ "vcc-lna-1p5", "vcc-bg", "vcc-quad", "vcc-mixer"
+};
+
+static int admv1014_freq_change(struct notifier_block *nb, unsigned long action, void *data)
+{
+ struct admv1014_state *st = container_of(nb, struct admv1014_state, nb);
+ int ret;
+
+ if (action == POST_RATE_CHANGE) {
+ mutex_lock(&st->lock);
+ ret = notifier_from_errno(admv1014_update_quad_filters(st));
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ return NOTIFY_OK;
+}
+
+#define _ADMV1014_EXT_INFO(_name, _shared, _ident) { \
+ .name = _name, \
+ .read = admv1014_read, \
+ .write = admv1014_write, \
+ .private = _ident, \
+ .shared = _shared, \
+}
+
+static const struct iio_chan_spec_ext_info admv1014_ext_info[] = {
+ _ADMV1014_EXT_INFO("calibscale_coarse", IIO_SEPARATE, ADMV1014_CALIBSCALE_COARSE),
+ _ADMV1014_EXT_INFO("calibscale_fine", IIO_SEPARATE, ADMV1014_CALIBSCALE_FINE),
+ { }
+};
+
+#define ADMV1014_CHAN_IQ(_channel, rf_comp) { \
+ .type = IIO_ALTVOLTAGE, \
+ .modified = 1, \
+ .output = 0, \
+ .indexed = 1, \
+ .channel2 = IIO_MOD_##rf_comp, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ }
+
+#define ADMV1014_CHAN_IF(_channel, rf_comp) { \
+ .type = IIO_ALTVOLTAGE, \
+ .modified = 1, \
+ .output = 0, \
+ .indexed = 1, \
+ .channel2 = IIO_MOD_##rf_comp, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ }
+
+#define ADMV1014_CHAN_POWER(_channel) { \
+ .type = IIO_POWER, \
+ .output = 0, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ }
+
+#define ADMV1014_CHAN_CALIBSCALE(_channel, rf_comp, _admv1014_ext_info) { \
+ .type = IIO_ALTVOLTAGE, \
+ .modified = 1, \
+ .output = 0, \
+ .indexed = 1, \
+ .channel2 = IIO_MOD_##rf_comp, \
+ .channel = _channel, \
+ .ext_info = _admv1014_ext_info, \
+ }
+
+static const struct iio_chan_spec admv1014_channels_iq[] = {
+ ADMV1014_CHAN_IQ(0, I),
+ ADMV1014_CHAN_IQ(0, Q),
+ ADMV1014_CHAN_POWER(0),
+};
+
+static const struct iio_chan_spec admv1014_channels_if[] = {
+ ADMV1014_CHAN_IF(0, I),
+ ADMV1014_CHAN_IF(0, Q),
+ ADMV1014_CHAN_CALIBSCALE(0, I, admv1014_ext_info),
+ ADMV1014_CHAN_CALIBSCALE(0, Q, admv1014_ext_info),
+ ADMV1014_CHAN_POWER(0),
+};
+
+static void admv1014_clk_disable(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static void admv1014_reg_disable(void *data)
+{
+ regulator_bulk_disable(ADMV1014_NUM_REGULATORS, data);
+}
+
+static void admv1014_powerdown(void *data)
+{
+ unsigned int enable_reg, enable_reg_msk;
+
+ /* Disable all components in the Enable Register */
+ enable_reg_msk = ADMV1014_IBIAS_PD_MSK |
+ ADMV1014_IF_AMP_PD_MSK |
+ ADMV1014_QUAD_BG_PD_MSK |
+ ADMV1014_BB_AMP_PD_MSK |
+ ADMV1014_QUAD_IBIAS_PD_MSK |
+ ADMV1014_BG_PD_MSK;
+
+ enable_reg = FIELD_PREP(ADMV1014_IBIAS_PD_MSK, 1) |
+ FIELD_PREP(ADMV1014_IF_AMP_PD_MSK, 1) |
+ FIELD_PREP(ADMV1014_QUAD_BG_PD_MSK, 1) |
+ FIELD_PREP(ADMV1014_BB_AMP_PD_MSK, 1) |
+ FIELD_PREP(ADMV1014_QUAD_IBIAS_PD_MSK, 1) |
+ FIELD_PREP(ADMV1014_BG_PD_MSK, 1);
+
+ admv1014_spi_update_bits(data, ADMV1014_REG_ENABLE,
+ enable_reg_msk, enable_reg);
+}
+
+static int admv1014_init(struct admv1014_state *st)
+{
+ unsigned int chip_id, enable_reg, enable_reg_msk;
+ struct spi_device *spi = st->spi;
+ int ret;
+
+ ret = regulator_bulk_enable(ADMV1014_NUM_REGULATORS, st->regulators);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable regulators");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, admv1014_reg_disable, st->regulators);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(st->clkin);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, admv1014_clk_disable, st->clkin);
+ if (ret)
+ return ret;
+
+ st->nb.notifier_call = admv1014_freq_change;
+ ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, admv1014_powerdown, st);
+ if (ret)
+ return ret;
+
+ /* Perform a software reset */
+ ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL,
+ ADMV1014_SPI_SOFT_RESET_MSK,
+ FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 1));
+ if (ret) {
+ dev_err(&spi->dev, "ADMV1014 SPI software reset failed.\n");
+ return ret;
+ }
+
+ ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL,
+ ADMV1014_SPI_SOFT_RESET_MSK,
+ FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 0));
+ if (ret) {
+ dev_err(&spi->dev, "ADMV1014 SPI software reset disable failed.\n");
+ return ret;
+ }
+
+ ret = __admv1014_spi_write(st, ADMV1014_REG_VVA_TEMP_COMP, 0x727C);
+ if (ret) {
+ dev_err(&spi->dev, "Writing default Temperature Compensation value failed.\n");
+ return ret;
+ }
+
+ ret = __admv1014_spi_read(st, ADMV1014_REG_SPI_CONTROL, &chip_id);
+ if (ret)
+ return ret;
+
+ chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id);
+ if (chip_id != ADMV1014_CHIP_ID) {
+ dev_err(&spi->dev, "Invalid Chip ID.\n");
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD,
+ ADMV1014_QUAD_SE_MODE_MSK,
+ FIELD_PREP(ADMV1014_QUAD_SE_MODE_MSK,
+ st->quad_se_mode));
+ if (ret) {
+ dev_err(&spi->dev, "Writing Quad SE Mode failed.\n");
+ return ret;
+ }
+
+ ret = admv1014_update_quad_filters(st);
+ if (ret) {
+ dev_err(&spi->dev, "Update Quad Filters failed.\n");
+ return ret;
+ }
+
+ ret = admv1014_update_vcm_settings(st);
+ if (ret) {
+ dev_err(&spi->dev, "Update VCM Settings failed.\n");
+ return ret;
+ }
+
+ enable_reg_msk = ADMV1014_P1DB_COMPENSATION_MSK |
+ ADMV1014_IF_AMP_PD_MSK |
+ ADMV1014_BB_AMP_PD_MSK |
+ ADMV1014_DET_EN_MSK;
+
+ enable_reg = FIELD_PREP(ADMV1014_P1DB_COMPENSATION_MSK, st->p1db_comp ? 3 : 0) |
+ FIELD_PREP(ADMV1014_IF_AMP_PD_MSK, !(st->input_mode)) |
+ FIELD_PREP(ADMV1014_BB_AMP_PD_MSK, st->input_mode) |
+ FIELD_PREP(ADMV1014_DET_EN_MSK, st->det_en);
+
+ return __admv1014_spi_update_bits(st, ADMV1014_REG_ENABLE, enable_reg_msk, enable_reg);
+}
+
+static int admv1014_properties_parse(struct admv1014_state *st)
+{
+ const char *str;
+ unsigned int i;
+ struct spi_device *spi = st->spi;
+ int ret;
+
+ st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable");
+
+ st->p1db_comp = device_property_read_bool(&spi->dev, "adi,p1db-compensation-enable");
+
+ ret = device_property_read_string(&spi->dev, "adi,input-mode", &str);
+ if (ret) {
+ st->input_mode = ADMV1014_IQ_MODE;
+ } else {
+ ret = match_string(input_mode_names, ARRAY_SIZE(input_mode_names), str);
+ if (ret < 0)
+ return ret;
+
+ st->input_mode = ret;
+ }
+
+ ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str);
+ if (ret) {
+ st->quad_se_mode = ADMV1014_SE_MODE_POS;
+ } else {
+ ret = match_string(quad_se_mode_names, ARRAY_SIZE(quad_se_mode_names), str);
+ if (ret < 0)
+ return ret;
+
+ st->quad_se_mode = ADMV1014_SE_MODE_POS + (ret * 3);
+ }
+
+ for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i)
+ st->regulators[i].supply = admv1014_reg_name[i];
+
+ ret = devm_regulator_bulk_get(&st->spi->dev, ADMV1014_NUM_REGULATORS,
+ st->regulators);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to request regulators");
+ return ret;
+ }
+
+ st->clkin = devm_clk_get(&spi->dev, "lo_in");
+ if (IS_ERR(st->clkin))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->clkin),
+ "failed to get the LO input clock\n");
+
+ return 0;
+}
+
+static int admv1014_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct admv1014_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ ret = admv1014_properties_parse(st);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &admv1014_info;
+ indio_dev->name = "admv1014";
+
+ if (st->input_mode == ADMV1014_IQ_MODE) {
+ indio_dev->channels = admv1014_channels_iq;
+ indio_dev->num_channels = ARRAY_SIZE(admv1014_channels_iq);
+ } else {
+ indio_dev->channels = admv1014_channels_if;
+ indio_dev->num_channels = ARRAY_SIZE(admv1014_channels_if);
+ }
+
+ st->spi = spi;
+
+ mutex_init(&st->lock);
+
+ ret = admv1014_init(st);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id admv1014_id[] = {
+ { "admv1014", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, admv1014_id);
+
+static const struct of_device_id admv1014_of_match[] = {
+ { .compatible = "adi,admv1014" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, admv1014_of_match);
+
+static struct spi_driver admv1014_driver = {
+ .driver = {
+ .name = "admv1014",
+ .of_match_table = admv1014_of_match,
+ },
+ .probe = admv1014_probe,
+ .id_table = admv1014_id,
+};
+module_spi_driver(admv1014_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
+MODULE_DESCRIPTION("Analog Devices ADMV1014");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/frequency/admv4420.c b/drivers/iio/frequency/admv4420.c
new file mode 100644
index 000000000000..51134aee8510
--- /dev/null
+++ b/drivers/iio/frequency/admv4420.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * ADMV4420
+ *
+ * Copyright 2021 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <asm/unaligned.h>
+
+/* ADMV4420 Register Map */
+#define ADMV4420_SPI_CONFIG_1 0x00
+#define ADMV4420_SPI_CONFIG_2 0x01
+#define ADMV4420_CHIPTYPE 0x03
+#define ADMV4420_PRODUCT_ID_L 0x04
+#define ADMV4420_PRODUCT_ID_H 0x05
+#define ADMV4420_SCRATCHPAD 0x0A
+#define ADMV4420_SPI_REV 0x0B
+#define ADMV4420_ENABLES 0x103
+#define ADMV4420_SDO_LEVEL 0x108
+#define ADMV4420_INT_L 0x200
+#define ADMV4420_INT_H 0x201
+#define ADMV4420_FRAC_L 0x202
+#define ADMV4420_FRAC_M 0x203
+#define ADMV4420_FRAC_H 0x204
+#define ADMV4420_MOD_L 0x208
+#define ADMV4420_MOD_M 0x209
+#define ADMV4420_MOD_H 0x20A
+#define ADMV4420_R_DIV_L 0x20C
+#define ADMV4420_R_DIV_H 0x20D
+#define ADMV4420_REFERENCE 0x20E
+#define ADMV4420_VCO_DATA_READBACK1 0x211
+#define ADMV4420_VCO_DATA_READBACK2 0x212
+#define ADMV4420_PLL_MUX_SEL 0x213
+#define ADMV4420_LOCK_DETECT 0x214
+#define ADMV4420_BAND_SELECT 0x215
+#define ADMV4420_VCO_ALC_TIMEOUT 0x216
+#define ADMV4420_VCO_MANUAL 0x217
+#define ADMV4420_ALC 0x219
+#define ADMV4420_VCO_TIMEOUT1 0x21C
+#define ADMV4420_VCO_TIMEOUT2 0x21D
+#define ADMV4420_VCO_BAND_DIV 0x21E
+#define ADMV4420_VCO_READBACK_SEL 0x21F
+#define ADMV4420_AUTOCAL 0x226
+#define ADMV4420_CP_STATE 0x22C
+#define ADMV4420_CP_BLEED_EN 0x22D
+#define ADMV4420_CP_CURRENT 0x22E
+#define ADMV4420_CP_BLEED 0x22F
+
+#define ADMV4420_SPI_CONFIG_1_SDOACTIVE (BIT(4) | BIT(3))
+#define ADMV4420_SPI_CONFIG_1_ENDIAN (BIT(5) | BIT(2))
+#define ADMV4420_SPI_CONFIG_1_SOFTRESET (BIT(7) | BIT(1))
+
+#define ADMV4420_REFERENCE_DIVIDE_BY_2_MASK BIT(0)
+#define ADMV4420_REFERENCE_MODE_MASK BIT(1)
+#define ADMV4420_REFERENCE_DOUBLER_MASK BIT(2)
+
+#define ADMV4420_REF_DIVIDER_MAX_VAL GENMASK(9, 0)
+#define ADMV4420_N_COUNTER_INT_MAX GENMASK(15, 0)
+#define ADMV4420_N_COUNTER_FRAC_MAX GENMASK(23, 0)
+#define ADMV4420_N_COUNTER_MOD_MAX GENMASK(23, 0)
+
+#define ENABLE_PLL BIT(6)
+#define ENABLE_LO BIT(5)
+#define ENABLE_VCO BIT(3)
+#define ENABLE_IFAMP BIT(2)
+#define ENABLE_MIXER BIT(1)
+#define ENABLE_LNA BIT(0)
+
+#define ADMV4420_SCRATCH_PAD_VAL_1 0xAD
+#define ADMV4420_SCRATCH_PAD_VAL_2 0xEA
+
+#define ADMV4420_REF_FREQ_HZ 50000000
+#define MAX_N_COUNTER 655360UL
+#define MAX_R_DIVIDER 1024
+#define ADMV4420_DEFAULT_LO_FREQ_HZ 16750000000ULL
+
+enum admv4420_mux_sel {
+ ADMV4420_LOW = 0,
+ ADMV4420_LOCK_DTCT = 1,
+ ADMV4420_R_COUNTER_PER_2 = 4,
+ ADMV4420_N_CONUTER_PER_2 = 5,
+ ADMV4420_HIGH = 8,
+};
+
+struct admv4420_reference_block {
+ bool doubler_en;
+ bool divide_by_2_en;
+ bool ref_single_ended;
+ u32 divider;
+};
+
+struct admv4420_n_counter {
+ u32 int_val;
+ u32 frac_val;
+ u32 mod_val;
+ u32 n_counter;
+};
+
+struct admv4420_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ u64 vco_freq_hz;
+ u64 lo_freq_hz;
+ struct admv4420_reference_block ref_block;
+ struct admv4420_n_counter n_counter;
+ enum admv4420_mux_sel mux_sel;
+ struct mutex lock;
+ u8 transf_buf[4] ____cacheline_aligned;
+};
+
+static const struct regmap_config admv4420_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .read_flag_mask = BIT(7),
+};
+
+static int admv4420_reg_access(struct iio_dev *indio_dev,
+ u32 reg, u32 writeval,
+ u32 *readval)
+{
+ struct admv4420_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int admv4420_set_n_counter(struct admv4420_state *st, u32 int_val,
+ u32 frac_val, u32 mod_val)
+{
+ int ret;
+
+ put_unaligned_le32(frac_val, st->transf_buf);
+ ret = regmap_bulk_write(st->regmap, ADMV4420_FRAC_L, st->transf_buf, 3);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(mod_val, st->transf_buf);
+ ret = regmap_bulk_write(st->regmap, ADMV4420_MOD_L, st->transf_buf, 3);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(int_val, st->transf_buf);
+ return regmap_bulk_write(st->regmap, ADMV4420_INT_L, st->transf_buf, 2);
+}
+
+static int admv4420_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct admv4420_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_FREQUENCY:
+
+ *val = div_u64_rem(st->lo_freq_hz, MICRO, val2);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info admv4420_info = {
+ .read_raw = admv4420_read_raw,
+ .debugfs_reg_access = &admv4420_reg_access,
+};
+
+static const struct iio_chan_spec admv4420_channels[] = {
+ {
+ .type = IIO_ALTVOLTAGE,
+ .output = 0,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
+ },
+};
+
+static void admv4420_fw_parse(struct admv4420_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ u32 tmp;
+ int ret;
+
+ ret = device_property_read_u32(dev, "adi,lo-freq-khz", &tmp);
+ if (!ret)
+ st->lo_freq_hz = (u64)tmp * KILO;
+
+ st->ref_block.ref_single_ended = device_property_read_bool(dev,
+ "adi,ref-ext-single-ended-en");
+}
+
+static inline uint64_t admv4420_calc_pfd_vco(struct admv4420_state *st)
+{
+ return div_u64(st->vco_freq_hz * 10, st->n_counter.n_counter);
+}
+
+static inline uint32_t admv4420_calc_pfd_ref(struct admv4420_state *st)
+{
+ uint32_t tmp;
+ u8 doubler, divide_by_2;
+
+ doubler = st->ref_block.doubler_en ? 2 : 1;
+ divide_by_2 = st->ref_block.divide_by_2_en ? 2 : 1;
+ tmp = ADMV4420_REF_FREQ_HZ * doubler;
+
+ return (tmp / (st->ref_block.divider * divide_by_2));
+}
+
+static int admv4420_calc_parameters(struct admv4420_state *st)
+{
+ u64 pfd_ref, pfd_vco;
+ bool sol_found = false;
+
+ st->ref_block.doubler_en = false;
+ st->ref_block.divide_by_2_en = false;
+ st->vco_freq_hz = div_u64(st->lo_freq_hz, 2);
+
+ for (st->ref_block.divider = 1; st->ref_block.divider < MAX_R_DIVIDER;
+ st->ref_block.divider++) {
+ pfd_ref = admv4420_calc_pfd_ref(st);
+ for (st->n_counter.n_counter = 1; st->n_counter.n_counter < MAX_N_COUNTER;
+ st->n_counter.n_counter++) {
+ pfd_vco = admv4420_calc_pfd_vco(st);
+ if (pfd_ref == pfd_vco) {
+ sol_found = true;
+ break;
+ }
+ }
+
+ if (sol_found)
+ break;
+
+ st->n_counter.n_counter = 1;
+ }
+ if (!sol_found)
+ return -1;
+
+ st->n_counter.int_val = div_u64_rem(st->n_counter.n_counter, 10, &st->n_counter.frac_val);
+ st->n_counter.mod_val = 10;
+
+ return 0;
+}
+
+static int admv4420_setup(struct iio_dev *indio_dev)
+{
+ struct admv4420_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ u32 val;
+ int ret;
+
+ ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
+ ADMV4420_SPI_CONFIG_1_SOFTRESET);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
+ ADMV4420_SPI_CONFIG_1_SDOACTIVE |
+ ADMV4420_SPI_CONFIG_1_ENDIAN);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap,
+ ADMV4420_SCRATCHPAD,
+ ADMV4420_SCRATCH_PAD_VAL_1);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
+ if (ret)
+ return ret;
+
+ if (val != ADMV4420_SCRATCH_PAD_VAL_1) {
+ dev_err(dev, "Failed ADMV4420 to read/write scratchpad %x ", val);
+ return -EIO;
+ }
+
+ ret = regmap_write(st->regmap,
+ ADMV4420_SCRATCHPAD,
+ ADMV4420_SCRATCH_PAD_VAL_2);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
+ if (ret)
+ return ret;
+
+ if (val != ADMV4420_SCRATCH_PAD_VAL_2) {
+ dev_err(dev, "Failed to read/write scratchpad %x ", val);
+ return -EIO;
+ }
+
+ st->mux_sel = ADMV4420_LOCK_DTCT;
+ st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ;
+
+ admv4420_fw_parse(st);
+
+ ret = admv4420_calc_parameters(st);
+ if (ret) {
+ dev_err(dev, "Failed calc parameters for %lld ", st->vco_freq_hz);
+ return ret;
+ }
+
+ ret = regmap_write(st->regmap, ADMV4420_R_DIV_L,
+ FIELD_GET(0xFF, st->ref_block.divider));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADMV4420_R_DIV_H,
+ FIELD_GET(0xFF00, st->ref_block.divider));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADMV4420_REFERENCE,
+ st->ref_block.divide_by_2_en |
+ FIELD_PREP(ADMV4420_REFERENCE_MODE_MASK, st->ref_block.ref_single_ended) |
+ FIELD_PREP(ADMV4420_REFERENCE_DOUBLER_MASK, st->ref_block.doubler_en));
+ if (ret)
+ return ret;
+
+ ret = admv4420_set_n_counter(st, st->n_counter.int_val,
+ st->n_counter.frac_val,
+ st->n_counter.mod_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADMV4420_PLL_MUX_SEL, st->mux_sel);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, ADMV4420_ENABLES,
+ ENABLE_PLL | ENABLE_LO | ENABLE_VCO |
+ ENABLE_IFAMP | ENABLE_MIXER | ENABLE_LNA);
+}
+
+static int admv4420_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct admv4420_state *st;
+ struct regmap *regmap;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap),
+ "Failed to initializing spi regmap\n");
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+ st->regmap = regmap;
+
+ indio_dev->name = "admv4420";
+ indio_dev->info = &admv4420_info;
+ indio_dev->channels = admv4420_channels;
+ indio_dev->num_channels = ARRAY_SIZE(admv4420_channels);
+
+ ret = admv4420_setup(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Setup ADMV4420 failed (%d)\n", ret);
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id admv4420_of_match[] = {
+ { .compatible = "adi,admv4420" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, admv4420_of_match);
+
+static struct spi_driver admv4420_driver = {
+ .driver = {
+ .name = "admv4420",
+ .of_match_table = admv4420_of_match,
+ },
+ .probe = admv4420_probe,
+};
+
+module_spi_driver(admv4420_driver);
+
+MODULE_AUTHOR("Cristian Pop <cristian.pop@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADMV44200 K Band Downconverter");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index a672f7d12bbb..97b86c4a53a6 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -139,30 +139,37 @@ config IIO_ST_GYRO_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_ST_SENSORS_CORE
- select IIO_ST_GYRO_I2C_3AXIS if (I2C)
- select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics gyroscopes:
L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330, LSM9DS0.
- This driver can also be built as a module. If so, these modules
- will be created:
- - st_gyro (core functions for the driver [it is mandatory]);
- - st_gyro_i2c (necessary for the I2C devices [optional*]);
- - st_gyro_spi (necessary for the SPI devices [optional*]);
-
- (*) one of these is necessary to do something.
+ Also need to enable at least one of I2C and SPI interface drivers
+ below.
config IIO_ST_GYRO_I2C_3AXIS
- tristate
- depends on IIO_ST_GYRO_3AXIS
- depends on IIO_ST_SENSORS_I2C
+ tristate "STMicroelectronics gyroscopes 3-Axis I2C Interface"
+ depends on I2C && IIO_ST_GYRO_3AXIS
+ default I2C && IIO_ST_GYRO_3AXIS
+ select IIO_ST_SENSORS_I2C
+ help
+ Build support for STMicroelectronics gyroscopes I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_gyro_i2c.
+
config IIO_ST_GYRO_SPI_3AXIS
- tristate
- depends on IIO_ST_GYRO_3AXIS
- depends on IIO_ST_SENSORS_SPI
+ tristate "STMicroelectronics gyroscopes 3-Axis SPI Interface"
+ depends on SPI_MASTER && IIO_ST_GYRO_3AXIS
+ default SPI_MASTER && IIO_ST_GYRO_3AXIS
+ select IIO_ST_SENSORS_SPI
+ help
+ Build support for STMicroelectronics gyroscopes SPI interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_gyro_spi.
+
config ITG3200
tristate "InvenSense ITG3200 Digital 3-Axis Gyroscope I2C driver"
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
index 36879f01e28c..71295709f2b9 100644
--- a/drivers/iio/gyro/adis16136.c
+++ b/drivers/iio/gyro/adis16136.c
@@ -591,3 +591,4 @@ module_spi_driver(adis16136_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c
index 66b6b7bd5e1b..eaf57bd339ed 100644
--- a/drivers/iio/gyro/adis16260.c
+++ b/drivers/iio/gyro/adis16260.c
@@ -433,3 +433,4 @@ module_spi_driver(adis16260_driver);
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
index 46ed12771d2f..5fd1bf9902ea 100644
--- a/drivers/iio/gyro/ssp_gyro_sensor.c
+++ b/drivers/iio/gyro/ssp_gyro_sensor.c
@@ -142,3 +142,4 @@ module_platform_driver(ssp_gyro_driver);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_SSP_SENSORS);
diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c
index 4ae33ef25b9c..1ebfe7aa6c96 100644
--- a/drivers/iio/gyro/st_gyro_buffer.c
+++ b/drivers/iio/gyro/st_gyro_buffer.c
@@ -7,7 +7,6 @@
* Denis Ciocca <denis.ciocca@st.com>
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@@ -65,6 +64,3 @@ int st_gyro_allocate_ring(struct iio_dev *indio_dev)
NULL, &st_sensors_trigger_handler, &st_gyro_buffer_setup_ops);
}
-MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics gyroscopes buffer");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
index 201050b76fe5..62172e18d0d8 100644
--- a/drivers/iio/gyro/st_gyro_core.c
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -472,7 +472,7 @@ const struct st_sensor_settings *st_gyro_get_settings(const char *name)
return &st_gyro_sensors_settings[index];
}
-EXPORT_SYMBOL(st_gyro_get_settings);
+EXPORT_SYMBOL_NS(st_gyro_get_settings, IIO_ST_SENSORS);
int st_gyro_common_probe(struct iio_dev *indio_dev)
{
@@ -518,8 +518,9 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
return devm_iio_device_register(parent, indio_dev);
}
-EXPORT_SYMBOL(st_gyro_common_probe);
+EXPORT_SYMBOL_NS(st_gyro_common_probe, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c
index 163c7ba300c1..8c7af42b6558 100644
--- a/drivers/iio/gyro/st_gyro_i2c.c
+++ b/drivers/iio/gyro/st_gyro_i2c.c
@@ -120,3 +120,4 @@ module_i2c_driver(st_gyro_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c
index b0023f9b9771..22aaabe48e4a 100644
--- a/drivers/iio/gyro/st_gyro_spi.c
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -124,3 +124,4 @@ module_spi_driver(st_gyro_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c
index 9a7819817488..c97e25448772 100644
--- a/drivers/iio/humidity/dht11.c
+++ b/drivers/iio/humidity/dht11.c
@@ -11,10 +11,9 @@
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/sysfs.h>
#include <linux/io.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
index 9e0fce917ce4..47f8e8ef56d6 100644
--- a/drivers/iio/humidity/hdc100x.c
+++ b/drivers/iio/humidity/hdc100x.c
@@ -417,10 +417,17 @@ static const struct of_device_id hdc100x_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, hdc100x_dt_ids);
+static const struct acpi_device_id hdc100x_acpi_match[] = {
+ { "TXNW1010" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, hdc100x_acpi_match);
+
static struct i2c_driver hdc100x_driver = {
.driver = {
.name = "hdc100x",
.of_match_table = hdc100x_dt_ids,
+ .acpi_match_table = hdc100x_acpi_match,
},
.probe = hdc100x_probe,
.id_table = hdc100x_id,
diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c
index 36df2a102ca4..fd9e2565f8a2 100644
--- a/drivers/iio/humidity/htu21.c
+++ b/drivers/iio/humidity/htu21.c
@@ -258,3 +258,4 @@ MODULE_DESCRIPTION("Measurement-Specialties htu21 temperature and humidity drive
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS);
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index cb0d66bf6561..f7fcfd04f659 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -30,8 +30,8 @@
* @value: The value to write to device (up to 4 bytes)
* @size: The size of the @value (in bytes)
*/
-int __adis_write_reg(struct adis *adis, unsigned int reg,
- unsigned int value, unsigned int size)
+int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value,
+ unsigned int size)
{
unsigned int page = reg / ADIS_PAGE_SIZE;
int ret, i;
@@ -114,14 +114,14 @@ int __adis_write_reg(struct adis *adis, unsigned int reg,
ret = spi_sync(adis->spi, &msg);
if (ret) {
dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
- reg, ret);
+ reg, ret);
} else {
adis->current_page = page;
}
return ret;
}
-EXPORT_SYMBOL_GPL(__adis_write_reg);
+EXPORT_SYMBOL_NS_GPL(__adis_write_reg, IIO_ADISLIB);
/**
* __adis_read_reg() - read N bytes from register (unlocked version)
@@ -130,8 +130,8 @@ EXPORT_SYMBOL_GPL(__adis_write_reg);
* @val: The value read back from the device
* @size: The size of the @val buffer
*/
-int __adis_read_reg(struct adis *adis, unsigned int reg,
- unsigned int *val, unsigned int size)
+int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val,
+ unsigned int size)
{
unsigned int page = reg / ADIS_PAGE_SIZE;
struct spi_message msg;
@@ -201,12 +201,12 @@ int __adis_read_reg(struct adis *adis, unsigned int reg,
ret = spi_sync(adis->spi, &msg);
if (ret) {
dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
- reg, ret);
+ reg, ret);
return ret;
- } else {
- adis->current_page = page;
}
+ adis->current_page = page;
+
switch (size) {
case 4:
*val = get_unaligned_be32(adis->rx);
@@ -218,7 +218,7 @@ int __adis_read_reg(struct adis *adis, unsigned int reg,
return ret;
}
-EXPORT_SYMBOL_GPL(__adis_read_reg);
+EXPORT_SYMBOL_NS_GPL(__adis_read_reg, IIO_ADISLIB);
/**
* __adis_update_bits_base() - ADIS Update bits function - Unlocked version
* @adis: The adis device
@@ -243,17 +243,17 @@ int __adis_update_bits_base(struct adis *adis, unsigned int reg, const u32 mask,
return __adis_write_reg(adis, reg, __val, size);
}
-EXPORT_SYMBOL_GPL(__adis_update_bits_base);
+EXPORT_SYMBOL_NS_GPL(__adis_update_bits_base, IIO_ADISLIB);
#ifdef CONFIG_DEBUG_FS
-int adis_debugfs_reg_access(struct iio_dev *indio_dev,
- unsigned int reg, unsigned int writeval, unsigned int *readval)
+int adis_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
{
struct adis *adis = iio_device_get_drvdata(indio_dev);
if (readval) {
- uint16_t val16;
+ u16 val16;
int ret;
ret = adis_read_reg_16(adis, reg, &val16);
@@ -261,11 +261,11 @@ int adis_debugfs_reg_access(struct iio_dev *indio_dev,
*readval = val16;
return ret;
- } else {
- return adis_write_reg_16(adis, reg, writeval);
}
+
+ return adis_write_reg_16(adis, reg, writeval);
}
-EXPORT_SYMBOL(adis_debugfs_reg_access);
+EXPORT_SYMBOL_NS(adis_debugfs_reg_access, IIO_ADISLIB);
#endif
@@ -279,14 +279,16 @@ EXPORT_SYMBOL(adis_debugfs_reg_access);
int adis_enable_irq(struct adis *adis, bool enable)
{
int ret = 0;
- uint16_t msc;
+ u16 msc;
mutex_lock(&adis->state_lock);
if (adis->data->enable_irq) {
ret = adis->data->enable_irq(adis, enable);
goto out_unlock;
- } else if (adis->data->unmasked_drdy) {
+ }
+
+ if (adis->data->unmasked_drdy) {
if (enable)
enable_irq(adis->spi->irq);
else
@@ -312,7 +314,7 @@ out_unlock:
mutex_unlock(&adis->state_lock);
return ret;
}
-EXPORT_SYMBOL(adis_enable_irq);
+EXPORT_SYMBOL_NS(adis_enable_irq, IIO_ADISLIB);
/**
* __adis_check_status() - Check the device for error conditions (unlocked)
@@ -322,7 +324,7 @@ EXPORT_SYMBOL(adis_enable_irq);
*/
int __adis_check_status(struct adis *adis)
{
- uint16_t status;
+ u16 status;
int ret;
int i;
@@ -344,7 +346,7 @@ int __adis_check_status(struct adis *adis)
return -EIO;
}
-EXPORT_SYMBOL_GPL(__adis_check_status);
+EXPORT_SYMBOL_NS_GPL(__adis_check_status, IIO_ADISLIB);
/**
* __adis_reset() - Reset the device (unlocked version)
@@ -358,7 +360,7 @@ int __adis_reset(struct adis *adis)
const struct adis_timeout *timeouts = adis->data->timeouts;
ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg,
- ADIS_GLOB_CMD_SW_RESET);
+ ADIS_GLOB_CMD_SW_RESET);
if (ret) {
dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
return ret;
@@ -368,7 +370,7 @@ int __adis_reset(struct adis *adis)
return 0;
}
-EXPORT_SYMBOL_GPL(__adis_reset);
+EXPORT_SYMBOL_NS_GPL(__adis_reset, IIO_ADIS_LIB);
static int adis_self_test(struct adis *adis)
{
@@ -414,7 +416,7 @@ int __adis_initial_startup(struct adis *adis)
{
const struct adis_timeout *timeouts = adis->data->timeouts;
struct gpio_desc *gpio;
- uint16_t prod_id;
+ u16 prod_id;
int ret;
/* check if the device has rst pin low */
@@ -423,7 +425,7 @@ int __adis_initial_startup(struct adis *adis)
return PTR_ERR(gpio);
if (gpio) {
- msleep(10);
+ usleep_range(10, 12);
/* bring device out of reset */
gpiod_set_value_cansleep(gpio, 0);
msleep(timeouts->reset_ms);
@@ -459,7 +461,7 @@ int __adis_initial_startup(struct adis *adis)
return 0;
}
-EXPORT_SYMBOL_GPL(__adis_initial_startup);
+EXPORT_SYMBOL_NS_GPL(__adis_initial_startup, IIO_ADISLIB);
/**
* adis_single_conversion() - Performs a single sample conversion
@@ -477,7 +479,8 @@ EXPORT_SYMBOL_GPL(__adis_initial_startup);
* a error bit in the channels raw value set error_mask to 0.
*/
int adis_single_conversion(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
+ const struct iio_chan_spec *chan,
+ unsigned int error_mask, int *val)
{
struct adis *adis = iio_device_get_drvdata(indio_dev);
unsigned int uval;
@@ -486,7 +489,7 @@ int adis_single_conversion(struct iio_dev *indio_dev,
mutex_lock(&adis->state_lock);
ret = __adis_read_reg(adis, chan->address, &uval,
- chan->scan_type.storagebits / 8);
+ chan->scan_type.storagebits / 8);
if (ret)
goto err_unlock;
@@ -506,7 +509,7 @@ err_unlock:
mutex_unlock(&adis->state_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(adis_single_conversion);
+EXPORT_SYMBOL_NS_GPL(adis_single_conversion, IIO_ADISLIB);
/**
* adis_init() - Initialize adis device structure
@@ -521,7 +524,7 @@ EXPORT_SYMBOL_GPL(adis_single_conversion);
* called.
*/
int adis_init(struct adis *adis, struct iio_dev *indio_dev,
- struct spi_device *spi, const struct adis_data *data)
+ struct spi_device *spi, const struct adis_data *data)
{
if (!data || !data->timeouts) {
dev_err(&spi->dev, "No config data or timeouts not defined!\n");
@@ -543,7 +546,7 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL_GPL(adis_init);
+EXPORT_SYMBOL_NS_GPL(adis_init, IIO_ADISLIB);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c
index 9fd30e62d6e8..17bb0c40a149 100644
--- a/drivers/iio/imu/adis16400.c
+++ b/drivers/iio/imu/adis16400.c
@@ -1240,3 +1240,4 @@ module_spi_driver(adis16400_driver);
MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c
index b01988170118..69facd72bd7d 100644
--- a/drivers/iio/imu/adis16460.c
+++ b/drivers/iio/imu/adis16460.c
@@ -428,3 +428,4 @@ module_spi_driver(adis16460_driver);
MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c
index ea91d127077d..ff2b0fab840a 100644
--- a/drivers/iio/imu/adis16475.c
+++ b/drivers/iio/imu/adis16475.c
@@ -1365,3 +1365,4 @@ module_spi_driver(adis16475_driver);
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADIS16475 IMU driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index f9b4540db1f4..44bbe3d19907 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -1538,3 +1538,4 @@ module_spi_driver(adis16480_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
index 351c303c8a8c..928933027ae3 100644
--- a/drivers/iio/imu/adis_buffer.c
+++ b/drivers/iio/imu/adis_buffer.c
@@ -20,7 +20,7 @@
#include <linux/iio/imu/adis.h>
static int adis_update_scan_mode_burst(struct iio_dev *indio_dev,
- const unsigned long *scan_mask)
+ const unsigned long *scan_mask)
{
struct adis *adis = iio_device_get_drvdata(indio_dev);
unsigned int burst_length, burst_max_length;
@@ -67,7 +67,7 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev,
}
int adis_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *scan_mask)
+ const unsigned long *scan_mask)
{
struct adis *adis = iio_device_get_drvdata(indio_dev);
const struct iio_chan_spec *chan;
@@ -124,7 +124,7 @@ int adis_update_scan_mode(struct iio_dev *indio_dev,
return 0;
}
-EXPORT_SYMBOL_GPL(adis_update_scan_mode);
+EXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, IIO_ADISLIB);
static irqreturn_t adis_trigger_handler(int irq, void *p)
{
@@ -158,7 +158,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
}
iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
- pf->timestamp);
+ pf->timestamp);
irq_done:
iio_trigger_notify_done(indio_dev->trig);
@@ -212,5 +212,5 @@ devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup,
adis);
}
-EXPORT_SYMBOL_GPL(devm_adis_setup_buffer_and_trigger);
+EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger, IIO_ADISLIB);
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
index c461bd1e8e69..f890bf842db8 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu/adis_trigger.c
@@ -15,8 +15,7 @@
#include <linux/iio/trigger.h>
#include <linux/iio/imu/adis.h>
-static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig,
- bool state)
+static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state)
{
struct adis *adis = iio_trigger_get_drvdata(trig);
@@ -88,5 +87,5 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
return devm_iio_trigger_register(&adis->spi->dev, adis->trig);
}
-EXPORT_SYMBOL_GPL(devm_adis_probe_trigger);
+EXPORT_SYMBOL_NS_GPL(devm_adis_probe_trigger, IIO_ADISLIB);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
index f8f0cf716bc6..9b4298095d3f 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
@@ -127,15 +127,14 @@ static int inv_mpu_process_acpi_config(struct i2c_client *client,
int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
{
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
+ struct acpi_device *adev = ACPI_COMPANION(&client->dev);
st->mux_client = NULL;
- if (ACPI_HANDLE(&client->dev)) {
+ if (adev) {
struct i2c_board_info info;
struct i2c_client *mux_client;
- struct acpi_device *adev;
int ret = -1;
- adev = ACPI_COMPANION(&client->dev);
memset(&info, 0, sizeof(info));
dmi_check_system(inv_mpu_dev_list);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
index fe03707ec2d3..55cffb5fa115 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
@@ -3,14 +3,14 @@
* Copyright (C) 2012 Invensense, Inc.
*/
-#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/property.h>
+
#include "inv_mpu_iio.h"
static const struct regmap_config inv_mpu_regmap_config = {
@@ -51,7 +51,7 @@ static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev)
{
struct inv_mpu6050_state *st = iio_priv(indio_dev);
struct device *dev = indio_dev->dev.parent;
- struct device_node *mux_node;
+ struct fwnode_handle *mux_node;
int ret;
/*
@@ -65,12 +65,12 @@ static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev)
case INV_MPU9150:
case INV_MPU9250:
case INV_MPU9255:
- mux_node = of_get_child_by_name(dev->of_node, "i2c-gate");
+ mux_node = device_get_named_child_node(dev, "i2c-gate");
if (mux_node != NULL) {
st->magn_disabled = true;
dev_warn(dev, "disable internal use of magnetometer\n");
}
- of_node_put(mux_node);
+ fwnode_handle_put(mux_node);
break;
default:
break;
@@ -249,11 +249,10 @@ static const struct of_device_id inv_of_match[] = {
};
MODULE_DEVICE_TABLE(of, inv_of_match);
-static const struct acpi_device_id __maybe_unused inv_acpi_match[] = {
+static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6500", INV_MPU6500},
{ },
};
-
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
static struct i2c_driver inv_mpu_driver = {
@@ -262,7 +261,7 @@ static struct i2c_driver inv_mpu_driver = {
.id_table = inv_mpu_id,
.driver = {
.of_match_table = inv_of_match,
- .acpi_match_table = ACPI_PTR(inv_acpi_match),
+ .acpi_match_table = inv_acpi_match,
.name = "inv-mpu6050-i2c",
.pm = &inv_mpu_pmops,
},
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
index 6800356b25fb..26a7c2521dc4 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
@@ -2,9 +2,8 @@
/*
* Copyright (C) 2015 Intel Corporation Inc.
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/acpi.h>
-#include <linux/of.h>
#include <linux/property.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
@@ -148,7 +147,7 @@ static struct spi_driver inv_mpu_driver = {
.id_table = inv_mpu_id,
.driver = {
.of_match_table = inv_of_match,
- .acpi_match_table = ACPI_PTR(inv_acpi_match),
+ .acpi_match_table = inv_acpi_match,
.name = "inv-mpu6000-spi",
.pm = &inv_mpu_pmops,
},
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c
index f89724481df9..ec23b1ee472b 100644
--- a/drivers/iio/imu/kmx61.c
+++ b/drivers/iio/imu/kmx61.c
@@ -1443,7 +1443,6 @@ static int kmx61_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int kmx61_suspend(struct device *dev)
{
int ret;
@@ -1469,9 +1468,7 @@ static int kmx61_resume(struct device *dev)
return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true);
}
-#endif
-#ifdef CONFIG_PM
static int kmx61_runtime_suspend(struct device *dev)
{
struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
@@ -1496,11 +1493,10 @@ static int kmx61_runtime_resume(struct device *dev)
return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true);
}
-#endif
static const struct dev_pm_ops kmx61_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume)
- SET_RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume)
+ RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL)
};
static const struct acpi_device_id kmx61_acpi_match[] = {
@@ -1521,7 +1517,7 @@ static struct i2c_driver kmx61_driver = {
.driver = {
.name = KMX61_DRV_NAME,
.acpi_match_table = ACPI_PTR(kmx61_acpi_match),
- .pm = &kmx61_pm_ops,
+ .pm = pm_ptr(&kmx61_pm_ops),
},
.probe = kmx61_probe,
.remove = kmx61_remove,
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 93f0c6bce502..b1d8d5a66f01 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -1633,7 +1633,7 @@ st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+ struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev));
const struct st_lsm6dsx_odr_table_entry *odr_table;
int i, len = 0;
@@ -1651,7 +1651,7 @@ static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+ struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev));
const struct st_lsm6dsx_fs_table_entry *fs_table;
struct st_lsm6dsx_hw *hw = sensor->hw;
int i, len = 0;
diff --git a/drivers/iio/imu/st_lsm9ds0/Kconfig b/drivers/iio/imu/st_lsm9ds0/Kconfig
index 53b7017014f8..d29558edee60 100644
--- a/drivers/iio/imu/st_lsm9ds0/Kconfig
+++ b/drivers/iio/imu/st_lsm9ds0/Kconfig
@@ -5,8 +5,6 @@ config IIO_ST_LSM9DS0
depends on (I2C || SPI_MASTER) && SYSFS
depends on !SENSORS_LIS3_I2C
depends on !SENSORS_LIS3_SPI
- select IIO_ST_LSM9DS0_I2C if I2C
- select IIO_ST_LSM9DS0_SPI if SPI_MASTER
select IIO_ST_ACCEL_3AXIS
select IIO_ST_MAGN_3AXIS
@@ -17,12 +15,30 @@ config IIO_ST_LSM9DS0
To compile this driver as a module, choose M here: the module
will be called st_lsm9ds0.
+ Also need to enable at least one of I2C and SPI interface drivers
+
config IIO_ST_LSM9DS0_I2C
- tristate
- depends on IIO_ST_LSM9DS0
+ tristate "STMicroelectronics LSM9DS0 IMU I2C interface"
+ depends on I2C && IIO_ST_LSM9DS0
+ default I2C && IIO_ST_LSM9DS0
+ select IIO_ST_ACCEL_I2C_3AXIS
+ select IIO_ST_MAGN_I2C_3AXIS
select REGMAP_I2C
+ help
+ Build support for STMicroelectronics LSM9DS0 IMU I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_lsm9ds0_i2c.
config IIO_ST_LSM9DS0_SPI
- tristate
- depends on IIO_ST_LSM9DS0
+ tristate "STMicroelectronics LSM9DS0 IMU SPI interface"
+ depends on SPI_MASTER && IIO_ST_LSM9DS0
+ default SPI_MASTER && IIO_ST_LSM9DS0
+ select IIO_ST_ACCEL_SPI_3AXIS
+ select IIO_ST_MAGN_SPI_3AXIS
select REGMAP_SPI
+ help
+ Build support for STMicroelectronics LSM9DS0 IMU I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_lsm9ds0_spi.
diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c
index 9fb06b7cde3c..ae7bc815382f 100644
--- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c
+++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c
@@ -142,8 +142,9 @@ int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap)
/* Setup magnetometer device */
return st_lsm9ds0_probe_magn(lsm9ds0, regmap);
}
-EXPORT_SYMBOL_GPL(st_lsm9ds0_probe);
+EXPORT_SYMBOL_NS_GPL(st_lsm9ds0_probe, IIO_ST_SENSORS);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU core driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c
index 8f205c477e6f..a90138d8b06a 100644
--- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c
+++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c
@@ -77,3 +77,4 @@ module_i2c_driver(st_lsm9ds0_driver);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU I2C driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c
index 0ddfa53166af..b743bf3546a7 100644
--- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c
+++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c
@@ -76,3 +76,4 @@ module_spi_driver(st_lsm9ds0_driver);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 208b5193c621..b078eb2f3c9d 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1383,9 +1383,9 @@ static ssize_t direction_show(struct device *dev,
switch (buffer->direction) {
case IIO_BUFFER_DIRECTION_IN:
- return sprintf(buf, "in\n");
+ return sysfs_emit(buf, "in\n");
case IIO_BUFFER_DIRECTION_OUT:
- return sprintf(buf, "out\n");
+ return sysfs_emit(buf, "out\n");
default:
return -EINVAL;
}
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 409c278a4c2c..e1ed44dec2ab 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -747,7 +747,7 @@ static ssize_t iio_read_channel_label(struct device *dev,
return indio_dev->info->read_label(indio_dev, this_attr->c, buf);
if (this_attr->c->extend_name)
- return sprintf(buf, "%s\n", this_attr->c->extend_name);
+ return sysfs_emit(buf, "%s\n", this_attr->c->extend_name);
return -EINVAL;
}
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index d0732eac0f0a..ce8b102ce52f 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -230,6 +230,7 @@ static const char * const iio_ev_type_text[] = {
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
[IIO_EV_TYPE_CHANGE] = "change",
+ [IIO_EV_TYPE_MAG_REFERENCED] = "mag_referenced",
};
static const char * const iio_ev_dir_text[] = {
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 0222885b334c..df74765d33dc 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -595,28 +595,50 @@ EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
int raw, int *processed, unsigned int scale)
{
- int scale_type, scale_val, scale_val2, offset;
+ int scale_type, scale_val, scale_val2;
+ int offset_type, offset_val, offset_val2;
s64 raw64 = raw;
- int ret;
- ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_OFFSET);
- if (ret >= 0)
- raw64 += offset;
+ offset_type = iio_channel_read(chan, &offset_val, &offset_val2,
+ IIO_CHAN_INFO_OFFSET);
+ if (offset_type >= 0) {
+ switch (offset_type) {
+ case IIO_VAL_INT:
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
+ case IIO_VAL_INT_PLUS_NANO:
+ /*
+ * Both IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO
+ * implicitely truncate the offset to it's integer form.
+ */
+ break;
+ case IIO_VAL_FRACTIONAL:
+ offset_val /= offset_val2;
+ break;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ offset_val >>= offset_val2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ raw64 += offset_val;
+ }
scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
IIO_CHAN_INFO_SCALE);
if (scale_type < 0) {
/*
- * Just pass raw values as processed if no scaling is
- * available.
+ * If no channel scaling is available apply consumer scale to
+ * raw value and return.
*/
- *processed = raw;
+ *processed = raw * scale;
return 0;
}
switch (scale_type) {
case IIO_VAL_INT:
- *processed = raw64 * scale_val;
+ *processed = raw64 * scale_val * scale;
break;
case IIO_VAL_INT_PLUS_MICRO:
if (scale_val2 < 0)
diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c
index baaf202dce05..0f9d77598997 100644
--- a/drivers/iio/light/apds9300.c
+++ b/drivers/iio/light/apds9300.c
@@ -466,7 +466,6 @@ static int apds9300_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int apds9300_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -493,11 +492,8 @@ static int apds9300_resume(struct device *dev)
return ret;
}
-static SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend, apds9300_resume);
-#define APDS9300_PM_OPS (&apds9300_pm_ops)
-#else
-#define APDS9300_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend,
+ apds9300_resume);
static const struct i2c_device_id apds9300_id[] = {
{ APDS9300_DRV_NAME, 0 },
@@ -509,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, apds9300_id);
static struct i2c_driver apds9300_driver = {
.driver = {
.name = APDS9300_DRV_NAME,
- .pm = APDS9300_PM_OPS,
+ .pm = pm_sleep_ptr(&apds9300_pm_ops),
},
.probe = apds9300_probe,
.remove = apds9300_remove,
diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c
index abbf2e662e7d..790d3d613979 100644
--- a/drivers/iio/light/bh1780.c
+++ b/drivers/iio/light/bh1780.c
@@ -221,7 +221,6 @@ static int bh1780_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
static int bh1780_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -256,14 +255,9 @@ static int bh1780_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
-static const struct dev_pm_ops bh1780_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(bh1780_runtime_suspend,
- bh1780_runtime_resume, NULL)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(bh1780_dev_pm_ops, bh1780_runtime_suspend,
+ bh1780_runtime_resume, NULL);
static const struct i2c_device_id bh1780_id[] = {
{ "bh1780", 0 },
@@ -284,7 +278,7 @@ static struct i2c_driver bh1780_driver = {
.id_table = bh1780_id,
.driver = {
.name = "bh1780",
- .pm = &bh1780_dev_pm_ops,
+ .pm = pm_ptr(&bh1780_dev_pm_ops),
.of_match_table = of_bh1780_match,
},
};
diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c
index 18a410340dc5..2c80a0535d2c 100644
--- a/drivers/iio/light/cm3232.c
+++ b/drivers/iio/light/cm3232.c
@@ -374,7 +374,6 @@ static const struct i2c_device_id cm3232_id[] = {
{}
};
-#ifdef CONFIG_PM_SLEEP
static int cm3232_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -403,9 +402,7 @@ static int cm3232_resume(struct device *dev)
return ret;
}
-static const struct dev_pm_ops cm3232_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)};
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(cm3232_pm_ops, cm3232_suspend, cm3232_resume);
MODULE_DEVICE_TABLE(i2c, cm3232_id);
@@ -419,9 +416,7 @@ static struct i2c_driver cm3232_driver = {
.driver = {
.name = "cm3232",
.of_match_table = cm3232_of_match,
-#ifdef CONFIG_PM_SLEEP
- .pm = &cm3232_pm_ops,
-#endif
+ .pm = pm_sleep_ptr(&cm3232_pm_ops),
},
.id_table = cm3232_id,
.probe = cm3232_probe,
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index 2689867467a8..b36f8b7ca68e 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -784,7 +784,6 @@ static int isl29018_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int isl29018_suspend(struct device *dev)
{
struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
@@ -830,11 +829,8 @@ static int isl29018_resume(struct device *dev)
return err;
}
-static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume);
-#define ISL29018_PM_OPS (&isl29018_pm_ops)
-#else
-#define ISL29018_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend,
+ isl29018_resume);
#ifdef CONFIG_ACPI
static const struct acpi_device_id isl29018_acpi_match[] = {
@@ -866,7 +862,7 @@ static struct i2c_driver isl29018_driver = {
.driver = {
.name = "isl29018",
.acpi_match_table = ACPI_PTR(isl29018_acpi_match),
- .pm = ISL29018_PM_OPS,
+ .pm = pm_sleep_ptr(&isl29018_pm_ops),
.of_match_table = isl29018_of_match,
},
.probe = isl29018_probe,
diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c
index ba53b50d711a..eb68a52aab82 100644
--- a/drivers/iio/light/isl29125.c
+++ b/drivers/iio/light/isl29125.c
@@ -311,7 +311,6 @@ static int isl29125_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int isl29125_suspend(struct device *dev)
{
struct isl29125_data *data = iio_priv(i2c_get_clientdata(
@@ -326,9 +325,9 @@ static int isl29125_resume(struct device *dev)
return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
data->conf1);
}
-#endif
-static SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend, isl29125_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend,
+ isl29125_resume);
static const struct i2c_device_id isl29125_id[] = {
{ "isl29125", 0 },
@@ -339,7 +338,7 @@ MODULE_DEVICE_TABLE(i2c, isl29125_id);
static struct i2c_driver isl29125_driver = {
.driver = {
.name = ISL29125_DRV_NAME,
- .pm = &isl29125_pm_ops,
+ .pm = pm_sleep_ptr(&isl29125_pm_ops),
},
.probe = isl29125_probe,
.remove = isl29125_remove,
diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c
index 724a0ec9f35c..a55194263d23 100644
--- a/drivers/iio/light/jsa1212.c
+++ b/drivers/iio/light/jsa1212.c
@@ -383,7 +383,6 @@ static int jsa1212_remove(struct i2c_client *client)
return jsa1212_power_off(data);
}
-#ifdef CONFIG_PM_SLEEP
static int jsa1212_suspend(struct device *dev)
{
struct jsa1212_data *data;
@@ -421,12 +420,8 @@ unlock_and_ret:
return ret;
}
-static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume);
-
-#define JSA1212_PM_OPS (&jsa1212_pm_ops)
-#else
-#define JSA1212_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend,
+ jsa1212_resume);
static const struct acpi_device_id jsa1212_acpi_match[] = {
{"JSA1212", 0},
@@ -443,7 +438,7 @@ MODULE_DEVICE_TABLE(i2c, jsa1212_id);
static struct i2c_driver jsa1212_driver = {
.driver = {
.name = JSA1212_DRIVER_NAME,
- .pm = JSA1212_PM_OPS,
+ .pm = pm_sleep_ptr(&jsa1212_pm_ops),
.acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
},
.probe = jsa1212_probe,
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 8a621244dd01..827bc25269e9 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -417,7 +417,7 @@ static ssize_t show_thresh_either_en(struct device *dev,
enable = 0;
}
- return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
+ return sysfs_emit(buf, "%u\n", enable);
}
static ssize_t store_thresh_either_en(struct device *dev,
@@ -474,7 +474,7 @@ static ssize_t show_zone(struct device *dev,
if (ret)
return ret;
- return scnprintf(buf, PAGE_SIZE, "%u\n", zone);
+ return sysfs_emit(buf, "%u\n", zone);
}
enum lm3533_als_attribute_type {
@@ -530,7 +530,7 @@ static ssize_t show_als_attr(struct device *dev,
if (ret)
return ret;
- return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+ return sysfs_emit(buf, "%u\n", val);
}
static ssize_t store_als_attr(struct device *dev,
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 47d61ec2bb50..679a1e1086ae 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -1611,7 +1611,6 @@ static int ltr501_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int ltr501_suspend(struct device *dev)
{
struct ltr501_data *data = iio_priv(i2c_get_clientdata(
@@ -1627,23 +1626,22 @@ static int ltr501_resume(struct device *dev)
return ltr501_write_contr(data, data->als_contr,
data->ps_contr);
}
-#endif
-static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
static const struct acpi_device_id ltr_acpi_match[] = {
- {"LTER0501", ltr501},
- {"LTER0559", ltr559},
- {"LTER0301", ltr301},
+ { "LTER0501", ltr501 },
+ { "LTER0559", ltr559 },
+ { "LTER0301", ltr301 },
{ },
};
MODULE_DEVICE_TABLE(acpi, ltr_acpi_match);
static const struct i2c_device_id ltr501_id[] = {
- { "ltr501", ltr501},
- { "ltr559", ltr559},
- { "ltr301", ltr301},
- { "ltr303", ltr303},
+ { "ltr501", ltr501 },
+ { "ltr559", ltr559 },
+ { "ltr301", ltr301 },
+ { "ltr303", ltr303 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltr501_id);
@@ -1661,7 +1659,7 @@ static struct i2c_driver ltr501_driver = {
.driver = {
.name = LTR501_DRV_NAME,
.of_match_table = ltr501_of_match,
- .pm = &ltr501_pm_ops,
+ .pm = pm_sleep_ptr(&ltr501_pm_ops),
.acpi_match_table = ACPI_PTR(ltr_acpi_match),
},
.probe = ltr501_probe,
diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c
index a52b2c788540..528fa5dd2b13 100644
--- a/drivers/iio/light/pa12203001.c
+++ b/drivers/iio/light/pa12203001.c
@@ -452,14 +452,14 @@ static const struct dev_pm_ops pa12203001_pm_ops = {
};
static const struct acpi_device_id pa12203001_acpi_match[] = {
- { "TXCPA122", 0},
+ { "TXCPA122", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match);
static const struct i2c_device_id pa12203001_id[] = {
- {"txcpa122", 0},
+ { "txcpa122", 0 },
{}
};
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index c2dd8a3d4217..dabdd05f0e2c 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -1055,7 +1055,6 @@ static int rpr0521_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
static int rpr0521_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -1101,11 +1100,9 @@ static int rpr0521_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops rpr0521_pm_ops = {
- SET_RUNTIME_PM_OPS(rpr0521_runtime_suspend,
- rpr0521_runtime_resume, NULL)
+ RUNTIME_PM_OPS(rpr0521_runtime_suspend, rpr0521_runtime_resume, NULL)
};
static const struct acpi_device_id rpr0521_acpi_match[] = {
@@ -1124,7 +1121,7 @@ MODULE_DEVICE_TABLE(i2c, rpr0521_id);
static struct i2c_driver rpr0521_driver = {
.driver = {
.name = RPR0521_DRV_NAME,
- .pm = &rpr0521_pm_ops,
+ .pm = pm_ptr(&rpr0521_pm_ops),
.acpi_match_table = ACPI_PTR(rpr0521_acpi_match),
},
.probe = rpr0521_probe,
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 41a2ce5a2d53..3d4cc1180b6a 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -323,7 +323,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
return devm_iio_device_register(dev, iio_dev);
}
-EXPORT_SYMBOL(st_uvis25_probe);
+EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25);
static int __maybe_unused st_uvis25_suspend(struct device *dev)
{
@@ -349,7 +349,7 @@ static int __maybe_unused st_uvis25_resume(struct device *dev)
const struct dev_pm_ops st_uvis25_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume)
};
-EXPORT_SYMBOL(st_uvis25_pm_ops);
+EXPORT_SYMBOL_NS(st_uvis25_pm_ops, IIO_UVIS25);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver");
diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c
index 98cd49eefe45..b06d09af28a3 100644
--- a/drivers/iio/light/st_uvis25_i2c.c
+++ b/drivers/iio/light/st_uvis25_i2c.c
@@ -66,3 +66,4 @@ module_i2c_driver(st_uvis25_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_UVIS25);
diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c
index af9d94d12787..3a4dc6d7180c 100644
--- a/drivers/iio/light/st_uvis25_spi.c
+++ b/drivers/iio/light/st_uvis25_spi.c
@@ -66,3 +66,4 @@ module_spi_driver(st_uvis25_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_UVIS25);
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index fc63856ed54d..1d02dfbc29d1 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -632,7 +632,6 @@ static int stk3310_remove(struct i2c_client *client)
return stk3310_set_state(iio_priv(indio_dev), STK3310_STATE_STANDBY);
}
-#ifdef CONFIG_PM_SLEEP
static int stk3310_suspend(struct device *dev)
{
struct stk3310_data *data;
@@ -656,12 +655,8 @@ static int stk3310_resume(struct device *dev)
return stk3310_set_state(data, state);
}
-static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume);
-
-#define STK3310_PM_OPS (&stk3310_pm_ops)
-#else
-#define STK3310_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend,
+ stk3310_resume);
static const struct i2c_device_id stk3310_i2c_id[] = {
{"STK3310", 0},
@@ -692,7 +687,7 @@ static struct i2c_driver stk3310_driver = {
.driver = {
.name = "stk3310",
.of_match_table = stk3310_of_match,
- .pm = STK3310_PM_OPS,
+ .pm = pm_sleep_ptr(&stk3310_pm_ops),
.acpi_match_table = ACPI_PTR(stk3310_acpi_id),
},
.probe = stk3310_probe,
diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c
index b87222141429..3951536022b3 100644
--- a/drivers/iio/light/tcs3414.c
+++ b/drivers/iio/light/tcs3414.c
@@ -345,7 +345,6 @@ static int tcs3414_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int tcs3414_suspend(struct device *dev)
{
struct tcs3414_data *data = iio_priv(i2c_get_clientdata(
@@ -360,9 +359,9 @@ static int tcs3414_resume(struct device *dev)
return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,
data->control);
}
-#endif
-static SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend, tcs3414_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend,
+ tcs3414_resume);
static const struct i2c_device_id tcs3414_id[] = {
{ "tcs3414", 0 },
@@ -373,7 +372,7 @@ MODULE_DEVICE_TABLE(i2c, tcs3414_id);
static struct i2c_driver tcs3414_driver = {
.driver = {
.name = TCS3414_DRV_NAME,
- .pm = &tcs3414_pm_ops,
+ .pm = pm_sleep_ptr(&tcs3414_pm_ops),
},
.probe = tcs3414_probe,
.id_table = tcs3414_id,
diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c
index 371c6a39a165..823435f59bb6 100644
--- a/drivers/iio/light/tcs3472.c
+++ b/drivers/iio/light/tcs3472.c
@@ -572,7 +572,6 @@ static int tcs3472_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int tcs3472_suspend(struct device *dev)
{
struct tcs3472_data *data = iio_priv(i2c_get_clientdata(
@@ -598,9 +597,9 @@ static int tcs3472_resume(struct device *dev)
return ret;
}
-#endif
-static SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, tcs3472_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend,
+ tcs3472_resume);
static const struct i2c_device_id tcs3472_id[] = {
{ "tcs3472", 0 },
@@ -611,7 +610,7 @@ MODULE_DEVICE_TABLE(i2c, tcs3472_id);
static struct i2c_driver tcs3472_driver = {
.driver = {
.name = TCS3472_DRV_NAME,
- .pm = &tcs3472_pm_ops,
+ .pm = pm_sleep_ptr(&tcs3472_pm_ops),
},
.probe = tcs3472_probe,
.remove = tcs3472_remove,
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 5bf2bfbc5379..0a278eea36ca 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -814,7 +814,6 @@ static int tsl2563_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int tsl2563_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -857,11 +856,8 @@ out:
return ret;
}
-static SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume);
-#define TSL2563_PM_OPS (&tsl2563_pm_ops)
-#else
-#define TSL2563_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend,
+ tsl2563_resume);
static const struct i2c_device_id tsl2563_id[] = {
{ "tsl2560", 0 },
@@ -885,7 +881,7 @@ static struct i2c_driver tsl2563_i2c_driver = {
.driver = {
.name = "tsl2563",
.of_match_table = tsl2563_of_match,
- .pm = TSL2563_PM_OPS,
+ .pm = pm_sleep_ptr(&tsl2563_pm_ops),
},
.probe = tsl2563_probe,
.remove = tsl2563_remove,
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index d79205361dfa..729f14d9f2a4 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -1902,7 +1902,7 @@ static const struct i2c_device_id tsl2772_idtable[] = {
{ "tmd2672", tmd2672 },
{ "tsl2772", tsl2772 },
{ "tmd2772", tmd2772 },
- { "apds9930", apds9930},
+ { "apds9930", apds9930 },
{}
};
diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c
index 70505ba6d858..6ae1b27e50b6 100644
--- a/drivers/iio/light/tsl4531.c
+++ b/drivers/iio/light/tsl4531.c
@@ -215,7 +215,6 @@ static int tsl4531_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int tsl4531_suspend(struct device *dev)
{
return tsl4531_powerdown(to_i2c_client(dev));
@@ -227,11 +226,8 @@ static int tsl4531_resume(struct device *dev)
TSL4531_MODE_NORMAL);
}
-static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume);
-#define TSL4531_PM_OPS (&tsl4531_pm_ops)
-#else
-#define TSL4531_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend,
+ tsl4531_resume);
static const struct i2c_device_id tsl4531_id[] = {
{ "tsl4531", 0 },
@@ -242,7 +238,7 @@ MODULE_DEVICE_TABLE(i2c, tsl4531_id);
static struct i2c_driver tsl4531_driver = {
.driver = {
.name = TSL4531_DRV_NAME,
- .pm = TSL4531_PM_OPS,
+ .pm = pm_sleep_ptr(&tsl4531_pm_ops),
},
.probe = tsl4531_probe,
.remove = tsl4531_remove,
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index 96e4a66ddf28..1492aaf8d84c 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -947,15 +947,15 @@ static const struct dev_pm_ops us5182d_pm_ops = {
};
static const struct acpi_device_id us5182d_acpi_match[] = {
- { "USD5182", 0},
+ { "USD5182", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match);
static const struct i2c_device_id us5182d_id[] = {
- {"usd5182", 0},
- {}
+ { "usd5182", 0 },
+ {}
};
MODULE_DEVICE_TABLE(i2c, us5182d_id);
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
index 0db306ee910e..da2bf622a67b 100644
--- a/drivers/iio/light/vcnl4035.c
+++ b/drivers/iio/light/vcnl4035.c
@@ -651,7 +651,7 @@ static const struct dev_pm_ops vcnl4035_pm_ops = {
};
static const struct i2c_device_id vcnl4035_id[] = {
- { "vcnl4035", 0},
+ { "vcnl4035", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vcnl4035_id);
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 565ee41ccb3a..54445365c4bc 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -117,30 +117,35 @@ config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_ST_SENSORS_CORE
- select IIO_ST_MAGN_I2C_3AXIS if (I2C)
- select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics magnetometers:
LSM303DLHC, LSM303DLM, LIS3MDL.
- This driver can also be built as a module. If so, these modules
- will be created:
- - st_magn (core functions for the driver [it is mandatory]);
- - st_magn_i2c (necessary for the I2C devices [optional*]);
- - st_magn_spi (necessary for the SPI devices [optional*]);
-
- (*) one of these is necessary to do something.
+ Also need to enable at least one of I2C and SPI interface drivers
+ below.
config IIO_ST_MAGN_I2C_3AXIS
- tristate
- depends on IIO_ST_MAGN_3AXIS
- depends on IIO_ST_SENSORS_I2C
+ tristate "STMicroelectronics magnetometers 3-Axis I2C Interface"
+ depends on I2C && IIO_ST_MAGN_3AXIS
+ default I2C && IIO_ST_MAGN_3AXIS
+ select IIO_ST_SENSORS_I2C
+ help
+ Build support for STMicroelectronics magnetometers I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_magn_i2c.
config IIO_ST_MAGN_SPI_3AXIS
- tristate
- depends on IIO_ST_MAGN_3AXIS
- depends on IIO_ST_SENSORS_SPI
+ tristate "STMicroelectronics magnetometers 3-Axis SPI Interface"
+ depends on SPI_MASTER && IIO_ST_MAGN_3AXIS
+ default SPI_MASTER && IIO_ST_MAGN_3AXIS
+ select IIO_ST_SENSORS_SPI
+ help
+ Build support for STMicroelectronics magnetometers SPI interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_magn_spi.
config SENSORS_HMC5843
tristate
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index 55879a20ae52..088f748b683e 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -1033,7 +1033,6 @@ static int ak8975_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
static int ak8975_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -1074,14 +1073,9 @@ static int ak8975_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
-static const struct dev_pm_ops ak8975_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(ak8975_runtime_suspend,
- ak8975_runtime_resume, NULL)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(ak8975_dev_pm_ops, ak8975_runtime_suspend,
+ ak8975_runtime_resume, NULL);
static const struct i2c_device_id ak8975_id[] = {
{"ak8975", AK8975},
@@ -1113,7 +1107,7 @@ MODULE_DEVICE_TABLE(of, ak8975_of_match);
static struct i2c_driver ak8975_driver = {
.driver = {
.name = "ak8975",
- .pm = &ak8975_dev_pm_ops,
+ .pm = pm_ptr(&ak8975_dev_pm_ops),
.of_match_table = ak8975_of_match,
.acpi_match_table = ak_acpi_match,
},
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
index 3d4d21f979fa..64e8b04e654b 100644
--- a/drivers/iio/magnetometer/bmc150_magn.c
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -226,7 +226,7 @@ const struct regmap_config bmc150_magn_regmap_config = {
.writeable_reg = bmc150_magn_is_writeable_reg,
.volatile_reg = bmc150_magn_is_volatile_reg,
};
-EXPORT_SYMBOL(bmc150_magn_regmap_config);
+EXPORT_SYMBOL_NS(bmc150_magn_regmap_config, IIO_BMC150_MAGN);
static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data,
enum bmc150_magn_power_modes mode,
@@ -983,7 +983,7 @@ err_poweroff:
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
return ret;
}
-EXPORT_SYMBOL(bmc150_magn_probe);
+EXPORT_SYMBOL_NS(bmc150_magn_probe, IIO_BMC150_MAGN);
int bmc150_magn_remove(struct device *dev)
{
@@ -1010,7 +1010,7 @@ int bmc150_magn_remove(struct device *dev)
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
return 0;
}
-EXPORT_SYMBOL(bmc150_magn_remove);
+EXPORT_SYMBOL_NS(bmc150_magn_remove, IIO_BMC150_MAGN);
#ifdef CONFIG_PM
static int bmc150_magn_runtime_suspend(struct device *dev)
@@ -1078,7 +1078,7 @@ const struct dev_pm_ops bmc150_magn_pm_ops = {
SET_RUNTIME_PM_OPS(bmc150_magn_runtime_suspend,
bmc150_magn_runtime_resume, NULL)
};
-EXPORT_SYMBOL(bmc150_magn_pm_ops);
+EXPORT_SYMBOL_NS(bmc150_magn_pm_ops, IIO_BMC150_MAGN);
MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c
index 876e96005e33..e39b89661ad1 100644
--- a/drivers/iio/magnetometer/bmc150_magn_i2c.c
+++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c
@@ -80,3 +80,4 @@ module_i2c_driver(bmc150_magn_driver);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMC150 I2C magnetometer driver");
+MODULE_IMPORT_NS(IIO_BMC150_MAGN);
diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c
index 4c570412d65c..882987721071 100644
--- a/drivers/iio/magnetometer/bmc150_magn_spi.c
+++ b/drivers/iio/magnetometer/bmc150_magn_spi.c
@@ -64,3 +64,4 @@ module_spi_driver(bmc150_magn_spi_driver);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
MODULE_DESCRIPTION("BMC150 magnetometer SPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_BMC150_MAGN);
diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c
index 5a730d9bdbb0..92eb2d156ddb 100644
--- a/drivers/iio/magnetometer/hmc5843_core.c
+++ b/drivers/iio/magnetometer/hmc5843_core.c
@@ -608,14 +608,14 @@ int hmc5843_common_suspend(struct device *dev)
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_SLEEP);
}
-EXPORT_SYMBOL(hmc5843_common_suspend);
+EXPORT_SYMBOL_NS(hmc5843_common_suspend, IIO_HMC5843);
int hmc5843_common_resume(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_CONVERSION_CONTINUOUS);
}
-EXPORT_SYMBOL(hmc5843_common_resume);
+EXPORT_SYMBOL_NS(hmc5843_common_resume, IIO_HMC5843);
int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
enum hmc5843_ids id, const char *name)
@@ -669,7 +669,7 @@ buffer_setup_err:
hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
return ret;
}
-EXPORT_SYMBOL(hmc5843_common_probe);
+EXPORT_SYMBOL_NS(hmc5843_common_probe, IIO_HMC5843);
void hmc5843_common_remove(struct device *dev)
{
@@ -681,7 +681,7 @@ void hmc5843_common_remove(struct device *dev)
/* sleep mode to save power */
hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
}
-EXPORT_SYMBOL(hmc5843_common_remove);
+EXPORT_SYMBOL_NS(hmc5843_common_remove, IIO_HMC5843);
MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com>");
MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 core driver");
diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c
index bc6e12f1d521..8d2ff8fc204d 100644
--- a/drivers/iio/magnetometer/hmc5843_i2c.c
+++ b/drivers/iio/magnetometer/hmc5843_i2c.c
@@ -105,3 +105,4 @@ module_i2c_driver(hmc5843_driver);
MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 i2c driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_HMC5843);
diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c
index a99dd9b33e95..8403f09aba39 100644
--- a/drivers/iio/magnetometer/hmc5843_spi.c
+++ b/drivers/iio/magnetometer/hmc5843_spi.c
@@ -100,3 +100,4 @@ module_spi_driver(hmc5843_driver);
MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
MODULE_DESCRIPTION("HMC5983 SPI driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_HMC5843);
diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c
index 17c62d806218..226439d0bfb5 100644
--- a/drivers/iio/magnetometer/mag3110.c
+++ b/drivers/iio/magnetometer/mag3110.c
@@ -573,7 +573,6 @@ static int mag3110_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int mag3110_suspend(struct device *dev)
{
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
@@ -623,11 +622,8 @@ static int mag3110_resume(struct device *dev)
data->ctrl_reg1);
}
-static SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume);
-#define MAG3110_PM_OPS (&mag3110_pm_ops)
-#else
-#define MAG3110_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend,
+ mag3110_resume);
static const struct i2c_device_id mag3110_id[] = {
{ "mag3110", 0 },
@@ -645,7 +641,7 @@ static struct i2c_driver mag3110_driver = {
.driver = {
.name = "mag3110",
.of_match_table = mag3110_of_match,
- .pm = MAG3110_PM_OPS,
+ .pm = pm_sleep_ptr(&mag3110_pm_ops),
},
.probe = mag3110_probe,
.remove = mag3110_remove,
diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c
index 65f3d1ed0d59..186edfcda0b7 100644
--- a/drivers/iio/magnetometer/mmc35240.c
+++ b/drivers/iio/magnetometer/mmc35240.c
@@ -521,7 +521,6 @@ static int mmc35240_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int mmc35240_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -548,11 +547,9 @@ static int mmc35240_resume(struct device *dev)
return 0;
}
-#endif
-static const struct dev_pm_ops mmc35240_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(mmc35240_pm_ops, mmc35240_suspend,
+ mmc35240_resume);
static const struct of_device_id mmc35240_of_match[] = {
{ .compatible = "memsic,mmc35240", },
@@ -576,7 +573,7 @@ static struct i2c_driver mmc35240_driver = {
.driver = {
.name = MMC35240_DRV_NAME,
.of_match_table = mmc35240_of_match,
- .pm = &mmc35240_pm_ops,
+ .pm = pm_sleep_ptr(&mmc35240_pm_ops),
.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
},
.probe = mmc35240_probe,
diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
index 13914273c999..26195733ea3e 100644
--- a/drivers/iio/magnetometer/rm3100-core.c
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -100,7 +100,7 @@ const struct regmap_access_table rm3100_readable_table = {
.yes_ranges = rm3100_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
};
-EXPORT_SYMBOL_GPL(rm3100_readable_table);
+EXPORT_SYMBOL_NS_GPL(rm3100_readable_table, IIO_RM3100);
static const struct regmap_range rm3100_writable_ranges[] = {
regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
@@ -110,7 +110,7 @@ const struct regmap_access_table rm3100_writable_table = {
.yes_ranges = rm3100_writable_ranges,
.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
};
-EXPORT_SYMBOL_GPL(rm3100_writable_table);
+EXPORT_SYMBOL_NS_GPL(rm3100_writable_table, IIO_RM3100);
static const struct regmap_range rm3100_volatile_ranges[] = {
regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
@@ -120,7 +120,7 @@ const struct regmap_access_table rm3100_volatile_table = {
.yes_ranges = rm3100_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
};
-EXPORT_SYMBOL_GPL(rm3100_volatile_table);
+EXPORT_SYMBOL_NS_GPL(rm3100_volatile_table, IIO_RM3100);
static irqreturn_t rm3100_thread_fn(int irq, void *d)
{
@@ -607,7 +607,7 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(rm3100_common_probe);
+EXPORT_SYMBOL_NS_GPL(rm3100_common_probe, IIO_RM3100);
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
index 1ac622c6d6c9..ba669ab7113d 100644
--- a/drivers/iio/magnetometer/rm3100-i2c.c
+++ b/drivers/iio/magnetometer/rm3100-i2c.c
@@ -52,3 +52,4 @@ module_i2c_driver(rm3100_driver);
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_RM3100);
diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
index 65d5eb9e4f5e..76dc9b66cd3c 100644
--- a/drivers/iio/magnetometer/rm3100-spi.c
+++ b/drivers/iio/magnetometer/rm3100-spi.c
@@ -62,3 +62,4 @@ module_spi_driver(rm3100_driver);
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_RM3100);
diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c
index cb43ccda808d..79987f42e8d9 100644
--- a/drivers/iio/magnetometer/st_magn_buffer.c
+++ b/drivers/iio/magnetometer/st_magn_buffer.c
@@ -7,7 +7,6 @@
* Denis Ciocca <denis.ciocca@st.com>
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@@ -45,6 +44,3 @@ int st_magn_allocate_ring(struct iio_dev *indio_dev)
NULL, &st_sensors_trigger_handler, &st_magn_buffer_setup_ops);
}
-MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics magnetometers buffer");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 0806a1e65ce4..74435f4a427d 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -606,7 +606,7 @@ const struct st_sensor_settings *st_magn_get_settings(const char *name)
return &st_magn_sensors_settings[index];
}
-EXPORT_SYMBOL(st_magn_get_settings);
+EXPORT_SYMBOL_NS(st_magn_get_settings, IIO_ST_SENSORS);
int st_magn_common_probe(struct iio_dev *indio_dev)
{
@@ -653,8 +653,9 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
return devm_iio_device_register(parent, indio_dev);
}
-EXPORT_SYMBOL(st_magn_common_probe);
+EXPORT_SYMBOL_NS(st_magn_common_probe, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics magnetometers driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
index 7237711fc09b..c5d8c303db4e 100644
--- a/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -115,3 +115,4 @@ module_i2c_driver(st_magn_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
index 489d4462862f..6ddc4318564a 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -106,3 +106,4 @@ module_spi_driver(st_magn_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig
index 832df8da2bc6..01dd3f858d99 100644
--- a/drivers/iio/potentiometer/Kconfig
+++ b/drivers/iio/potentiometer/Kconfig
@@ -27,11 +27,11 @@ config AD5272
module will be called ad5272.
config DS1803
- tristate "Maxim Integrated DS1803 Digital Potentiometer driver"
+ tristate "Maxim Integrated DS1803 and similar Digital Potentiometer driver"
depends on I2C
help
- Say yes here to build support for the Maxim Integrated DS1803
- digital potentiometer chip.
+ Say yes here to build support for the Maxim Integrated DS1803 and
+ DS3502 digital potentiometer chip.
To compile this driver as a module, choose M here: the
module will be called ds1803.
diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c
index 20b45407eaac..5c212ed7a931 100644
--- a/drivers/iio/potentiometer/ds1803.c
+++ b/drivers/iio/potentiometer/ds1803.c
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Maxim Integrated DS1803 digital potentiometer driver
+ * Maxim Integrated DS1803 and similar digital potentiometer driver
* Copyright (c) 2016 Slawomir Stepien
+ * Copyright (c) 2022 Jagath Jog J
*
* Datasheet: https://datasheets.maximintegrated.com/en/ds/DS1803.pdf
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3502.pdf
*
* DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address
* ds1803 2 256 10, 50, 100 0101xxx
+ * ds3502 1 128 10 01010xx
*/
#include <linux/err.h>
@@ -15,24 +18,27 @@
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/property.h>
-#define DS1803_MAX_POS 255
-#define DS1803_WRITE(chan) (0xa8 | ((chan) + 1))
+#define DS1803_WIPER_0 0xA9
+#define DS1803_WIPER_1 0xAA
+#define DS3502_WR_IVR 0x00
enum ds1803_type {
DS1803_010,
DS1803_050,
DS1803_100,
+ DS3502,
};
struct ds1803_cfg {
+ int wipers;
+ int avail[3];
int kohms;
-};
-
-static const struct ds1803_cfg ds1803_cfg[] = {
- [DS1803_010] = { .kohms = 10, },
- [DS1803_050] = { .kohms = 50, },
- [DS1803_100] = { .kohms = 100, },
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+ int (*read)(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val);
};
struct ds1803_data {
@@ -40,42 +46,110 @@ struct ds1803_data {
const struct ds1803_cfg *cfg;
};
-#define DS1803_CHANNEL(ch) { \
- .type = IIO_RESISTANCE, \
- .indexed = 1, \
- .output = 1, \
- .channel = (ch), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+#define DS1803_CHANNEL(ch, addr) { \
+ .type = IIO_RESISTANCE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (ch), \
+ .address = (addr), \
+ .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_RAW), \
}
static const struct iio_chan_spec ds1803_channels[] = {
- DS1803_CHANNEL(0),
- DS1803_CHANNEL(1),
+ DS1803_CHANNEL(0, DS1803_WIPER_0),
+ DS1803_CHANNEL(1, DS1803_WIPER_1),
};
-static int ds1803_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
+static const struct iio_chan_spec ds3502_channels[] = {
+ DS1803_CHANNEL(0, DS3502_WR_IVR),
+};
+
+static int ds1803_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
{
struct ds1803_data *data = iio_priv(indio_dev);
- int pot = chan->channel;
int ret;
u8 result[ARRAY_SIZE(ds1803_channels)];
+ ret = i2c_master_recv(data->client, result, indio_dev->num_channels);
+ if (ret < 0)
+ return ret;
+
+ *val = result[chan->channel];
+ return ret;
+}
+
+static int ds3502_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct ds1803_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, chan->address);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return ret;
+}
+
+static const struct ds1803_cfg ds1803_cfg[] = {
+ [DS1803_010] = {
+ .wipers = 2,
+ .avail = { 0, 1, 255 },
+ .kohms = 10,
+ .channels = ds1803_channels,
+ .num_channels = ARRAY_SIZE(ds1803_channels),
+ .read = ds1803_read,
+ },
+ [DS1803_050] = {
+ .wipers = 2,
+ .avail = { 0, 1, 255 },
+ .kohms = 50,
+ .channels = ds1803_channels,
+ .num_channels = ARRAY_SIZE(ds1803_channels),
+ .read = ds1803_read,
+ },
+ [DS1803_100] = {
+ .wipers = 2,
+ .avail = { 0, 1, 255 },
+ .kohms = 100,
+ .channels = ds1803_channels,
+ .num_channels = ARRAY_SIZE(ds1803_channels),
+ .read = ds1803_read,
+ },
+ [DS3502] = {
+ .wipers = 1,
+ .avail = { 0, 1, 127 },
+ .kohms = 10,
+ .channels = ds3502_channels,
+ .num_channels = ARRAY_SIZE(ds3502_channels),
+ .read = ds3502_read,
+ },
+};
+
+static int ds1803_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ds1803_data *data = iio_priv(indio_dev);
+ int ret;
+
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = i2c_master_recv(data->client, result,
- indio_dev->num_channels);
+ ret = data->cfg->read(indio_dev, chan, val);
if (ret < 0)
return ret;
- *val = result[pot];
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 1000 * data->cfg->kohms;
- *val2 = DS1803_MAX_POS;
+ *val2 = data->cfg->avail[2]; /* Max wiper position */
return IIO_VAL_FRACTIONAL;
}
@@ -83,34 +157,52 @@ static int ds1803_read_raw(struct iio_dev *indio_dev,
}
static int ds1803_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 ds1803_data *data = iio_priv(indio_dev);
- int pot = chan->channel;
+ u8 addr = chan->address;
+ int max_pos = data->cfg->avail[2];
if (val2 != 0)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (val > DS1803_MAX_POS || val < 0)
+ if (val > max_pos || val < 0)
return -EINVAL;
break;
default:
return -EINVAL;
}
- return i2c_smbus_write_byte_data(data->client, DS1803_WRITE(pot), val);
+ return i2c_smbus_write_byte_data(data->client, addr, val);
+}
+
+static int ds1803_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ struct ds1803_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *vals = data->cfg->avail;
+ *length = ARRAY_SIZE(data->cfg->avail);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ }
+ return -EINVAL;
}
static const struct iio_info ds1803_info = {
.read_raw = ds1803_read_raw,
.write_raw = ds1803_write_raw,
+ .read_avail = ds1803_read_avail,
};
-static int ds1803_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ds1803_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct ds1803_data *data;
@@ -124,11 +216,13 @@ static int ds1803_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
- data->cfg = &ds1803_cfg[id->driver_data];
+ data->cfg = device_get_match_data(dev);
+ if (!data->cfg)
+ data->cfg = &ds1803_cfg[id->driver_data];
indio_dev->info = &ds1803_info;
- indio_dev->channels = ds1803_channels;
- indio_dev->num_channels = ARRAY_SIZE(ds1803_channels);
+ indio_dev->channels = data->cfg->channels;
+ indio_dev->num_channels = data->cfg->num_channels;
indio_dev->name = client->name;
return devm_iio_device_register(dev, indio_dev);
@@ -138,6 +232,7 @@ static const struct of_device_id ds1803_dt_ids[] = {
{ .compatible = "maxim,ds1803-010", .data = &ds1803_cfg[DS1803_010] },
{ .compatible = "maxim,ds1803-050", .data = &ds1803_cfg[DS1803_050] },
{ .compatible = "maxim,ds1803-100", .data = &ds1803_cfg[DS1803_100] },
+ { .compatible = "maxim,ds3502", .data = &ds1803_cfg[DS3502] },
{}
};
MODULE_DEVICE_TABLE(of, ds1803_dt_ids);
@@ -146,6 +241,7 @@ static const struct i2c_device_id ds1803_id[] = {
{ "ds1803-010", DS1803_010 },
{ "ds1803-050", DS1803_050 },
{ "ds1803-100", DS1803_100 },
+ { "ds3502", DS3502 },
{}
};
MODULE_DEVICE_TABLE(i2c, ds1803_id);
@@ -162,5 +258,6 @@ static struct i2c_driver ds1803_driver = {
module_i2c_driver(ds1803_driver);
MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>");
+MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>");
MODULE_DESCRIPTION("DS1803 digital potentiometer");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index fc0d3cfca418..0ff756cea63a 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -194,30 +194,35 @@ config IIO_ST_PRESS
tristate "STMicroelectronics pressure sensor Driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_ST_SENSORS_CORE
- select IIO_ST_PRESS_I2C if (I2C)
- select IIO_ST_PRESS_SPI if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics pressure
sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH.
- This driver can also be built as a module. If so, these modules
- will be created:
- - st_pressure (core functions for the driver [it is mandatory]);
- - st_pressure_i2c (necessary for the I2C devices [optional*]);
- - st_pressure_spi (necessary for the SPI devices [optional*]);
-
- (*) one of these is necessary to do something.
+ Also need to enable at least one of I2C and SPI interface drivers
+ below.
config IIO_ST_PRESS_I2C
- tristate
- depends on IIO_ST_PRESS
- depends on IIO_ST_SENSORS_I2C
+ tristate "STMicroelectronics pressure sensor I2C Interface"
+ depends on I2C && IIO_ST_PRESS
+ default I2C && IIO_ST_PRESS
+ select IIO_ST_SENSORS_I2C
+ help
+ Build support for STMicroelectronics pressure sensor I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_pressure_i2c.
config IIO_ST_PRESS_SPI
- tristate
- depends on IIO_ST_PRESS
- depends on IIO_ST_SENSORS_SPI
+ tristate "STMicroelectronics pressure sensor SPI Interface"
+ depends on SPI_MASTER && IIO_ST_PRESS
+ default SPI_MASTER && IIO_ST_PRESS
+ select IIO_ST_SENSORS_SPI
+ help
+ Build support for STMicroelectronics pressure sensor SPI interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_pressure_spi.
config T5403
tristate "EPCOS T5403 digital barometric pressure sensor driver"
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
index 0730380ceb69..36fb7ae0d0a9 100644
--- a/drivers/iio/pressure/dps310.c
+++ b/drivers/iio/pressure/dps310.c
@@ -812,9 +812,16 @@ static const struct i2c_device_id dps310_id[] = {
};
MODULE_DEVICE_TABLE(i2c, dps310_id);
+static const struct acpi_device_id dps310_acpi_match[] = {
+ { "IFX3100" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, dps310_acpi_match);
+
static struct i2c_driver dps310_driver = {
.driver = {
.name = DPS310_DEV_NAME,
+ .acpi_match_table = dps310_acpi_match,
},
.probe = dps310_probe,
.id_table = dps310_id,
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index 81f288312a28..5bf5b9abe6f1 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -187,7 +187,7 @@ int mpl115_probe(struct device *dev, const char *name,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(mpl115_probe);
+EXPORT_SYMBOL_NS_GPL(mpl115_probe, IIO_MPL115);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c
index ac1f12bcb65e..099ab1c6832c 100644
--- a/drivers/iio/pressure/mpl115_i2c.c
+++ b/drivers/iio/pressure/mpl115_i2c.c
@@ -62,3 +62,4 @@ module_i2c_driver(mpl115_i2c_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_MPL115);
diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c
index 4d064f98f56a..7feec87e2704 100644
--- a/drivers/iio/pressure/mpl115_spi.c
+++ b/drivers/iio/pressure/mpl115_spi.c
@@ -101,3 +101,4 @@ module_spi_driver(mpl115_spi_driver);
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_MPL115);
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c
index e95b9a5475b4..d4f89e4babed 100644
--- a/drivers/iio/pressure/mpl3115.c
+++ b/drivers/iio/pressure/mpl3115.c
@@ -301,7 +301,6 @@ static int mpl3115_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int mpl3115_suspend(struct device *dev)
{
return mpl3115_standby(iio_priv(i2c_get_clientdata(
@@ -317,11 +316,8 @@ static int mpl3115_resume(struct device *dev)
data->ctrl_reg1);
}
-static SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume);
-#define MPL3115_PM_OPS (&mpl3115_pm_ops)
-#else
-#define MPL3115_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend,
+ mpl3115_resume);
static const struct i2c_device_id mpl3115_id[] = {
{ "mpl3115", 0 },
@@ -339,7 +335,7 @@ static struct i2c_driver mpl3115_driver = {
.driver = {
.name = "mpl3115",
.of_match_table = mpl3115_of_match,
- .pm = MPL3115_PM_OPS,
+ .pm = pm_sleep_ptr(&mpl3115_pm_ops),
},
.probe = mpl3115_probe,
.remove = mpl3115_remove,
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
index a4d0b54cde9b..717521de66c4 100644
--- a/drivers/iio/pressure/ms5611_core.c
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -471,7 +471,7 @@ err_fini:
ms5611_fini(indio_dev);
return ret;
}
-EXPORT_SYMBOL(ms5611_probe);
+EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611);
void ms5611_remove(struct iio_dev *indio_dev)
{
@@ -479,7 +479,7 @@ void ms5611_remove(struct iio_dev *indio_dev)
iio_triggered_buffer_cleanup(indio_dev);
ms5611_fini(indio_dev);
}
-EXPORT_SYMBOL(ms5611_remove);
+EXPORT_SYMBOL_NS(ms5611_remove, IIO_MS5611);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("MS5611 core driver");
diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c
index 1047a85527a9..3b1de71e0d15 100644
--- a/drivers/iio/pressure/ms5611_i2c.c
+++ b/drivers/iio/pressure/ms5611_i2c.c
@@ -140,3 +140,4 @@ module_i2c_driver(ms5611_driver);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("MS5611 i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MS5611);
diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
index 7ccd960ced5d..432e912096f4 100644
--- a/drivers/iio/pressure/ms5611_spi.c
+++ b/drivers/iio/pressure/ms5611_spi.c
@@ -140,3 +140,4 @@ module_spi_driver(ms5611_driver);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("MS5611 spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MS5611);
diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c
index 81f683321b23..70c70019142a 100644
--- a/drivers/iio/pressure/ms5637.c
+++ b/drivers/iio/pressure/ms5637.c
@@ -252,3 +252,4 @@ MODULE_DESCRIPTION("Measurement-Specialties ms5637 temperature & pressure driver
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS);
diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c
index 25dbd5476b26..0dbf357c2c22 100644
--- a/drivers/iio/pressure/st_pressure_buffer.c
+++ b/drivers/iio/pressure/st_pressure_buffer.c
@@ -7,7 +7,6 @@
* Denis Ciocca <denis.ciocca@st.com>
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@@ -44,7 +43,3 @@ int st_press_allocate_ring(struct iio_dev *indio_dev)
return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
NULL, &st_sensors_trigger_handler, &st_press_buffer_setup_ops);
}
-
-MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
-MODULE_DESCRIPTION("STMicroelectronics pressures buffer");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 26a1ee43d56e..5b93933a2e27 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -672,7 +672,7 @@ const struct st_sensor_settings *st_press_get_settings(const char *name)
return &st_press_sensors_settings[index];
}
-EXPORT_SYMBOL(st_press_get_settings);
+EXPORT_SYMBOL_NS(st_press_get_settings, IIO_ST_SENSORS);
int st_press_common_probe(struct iio_dev *indio_dev)
{
@@ -724,8 +724,9 @@ int st_press_common_probe(struct iio_dev *indio_dev)
return devm_iio_device_register(parent, indio_dev);
}
-EXPORT_SYMBOL(st_press_common_probe);
+EXPORT_SYMBOL_NS(st_press_common_probe, IIO_ST_SENSORS);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics pressures driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c
index 1939e999a427..7035777fd988 100644
--- a/drivers/iio/pressure/st_pressure_i2c.c
+++ b/drivers/iio/pressure/st_pressure_i2c.c
@@ -120,3 +120,4 @@ module_i2c_driver(st_press_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics pressures i2c driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
index d6fc954e28f8..bfab8e7fb061 100644
--- a/drivers/iio/pressure/st_pressure_spi.c
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -118,3 +118,4 @@ module_spi_driver(st_press_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics pressures spi driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ST_SENSORS);
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
index 89295c90f801..67119a9b95fc 100644
--- a/drivers/iio/pressure/zpa2326.c
+++ b/drivers/iio/pressure/zpa2326.c
@@ -162,7 +162,7 @@ bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg)
return false;
}
}
-EXPORT_SYMBOL_GPL(zpa2326_isreg_writeable);
+EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_writeable, IIO_ZPA2326);
bool zpa2326_isreg_readable(struct device *dev, unsigned int reg)
{
@@ -191,7 +191,7 @@ bool zpa2326_isreg_readable(struct device *dev, unsigned int reg)
return false;
}
}
-EXPORT_SYMBOL_GPL(zpa2326_isreg_readable);
+EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_readable, IIO_ZPA2326);
bool zpa2326_isreg_precious(struct device *dev, unsigned int reg)
{
@@ -204,7 +204,7 @@ bool zpa2326_isreg_precious(struct device *dev, unsigned int reg)
return false;
}
}
-EXPORT_SYMBOL_GPL(zpa2326_isreg_precious);
+EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_precious, IIO_ZPA2326);
/**
* zpa2326_enable_device() - Enable device, i.e. get out of low power mode.
@@ -649,7 +649,7 @@ const struct dev_pm_ops zpa2326_pm_ops = {
SET_RUNTIME_PM_OPS(zpa2326_runtime_suspend, zpa2326_runtime_resume,
NULL)
};
-EXPORT_SYMBOL_GPL(zpa2326_pm_ops);
+EXPORT_SYMBOL_NS_GPL(zpa2326_pm_ops, IIO_ZPA2326);
/**
* zpa2326_resume() - Request the PM layer to power supply the device.
@@ -1698,7 +1698,7 @@ poweroff:
return err;
}
-EXPORT_SYMBOL_GPL(zpa2326_probe);
+EXPORT_SYMBOL_NS_GPL(zpa2326_probe, IIO_ZPA2326);
void zpa2326_remove(const struct device *parent)
{
@@ -1709,7 +1709,7 @@ void zpa2326_remove(const struct device *parent)
zpa2326_sleep(indio_dev);
zpa2326_power_off(indio_dev, iio_priv(indio_dev));
}
-EXPORT_SYMBOL_GPL(zpa2326_remove);
+EXPORT_SYMBOL_NS_GPL(zpa2326_remove, IIO_ZPA2326);
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
MODULE_DESCRIPTION("Core driver for Murata ZPA2326 pressure sensor");
diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c
index 95d9739444c4..0db0860d386b 100644
--- a/drivers/iio/pressure/zpa2326_i2c.c
+++ b/drivers/iio/pressure/zpa2326_i2c.c
@@ -87,3 +87,4 @@ module_i2c_driver(zpa2326_i2c_driver);
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ZPA2326);
diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c
index ee8ed77536ca..9c1bcb82d360 100644
--- a/drivers/iio/pressure/zpa2326_spi.c
+++ b/drivers/iio/pressure/zpa2326_spi.c
@@ -89,3 +89,4 @@ module_spi_driver(zpa2326_spi_driver);
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ZPA2326);
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index 7c7203ca3ac6..0e5c17530b8b 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -112,11 +112,17 @@ config SRF04
To compile this driver as a module, choose M here: the
module will be called srf04.
+config SX_COMMON
+ tristate
+ help
+ Common Semtech proximity sensor code.
+
config SX9310
tristate "SX9310/SX9311 Semtech proximity sensor"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP_I2C
+ select SX_COMMON
depends on I2C
help
Say Y here to build a driver for Semtech's SX9310/SX9311 capacitive
@@ -125,6 +131,34 @@ config SX9310
To compile this driver as a module, choose M here: the
module will be called sx9310.
+config SX9324
+ tristate "SX9324 Semtech proximity sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ select SX_COMMON
+ depends on I2C
+ help
+ Say Y here to build a driver for Semtech's SX9324
+ proximity/button sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sx9324.
+
+config SX9360
+ tristate "SX9360 Semtech proximity sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ select SX_COMMON
+ depends on I2C
+ help
+ Say Y here to build a driver for Semtech's SX9360
+ proximity/button sensor, a simplified SX9324.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sx9360.
+
config SX9500
tristate "SX9500 Semtech proximity sensor"
select IIO_BUFFER
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index cbdac09433eb..cc838bb5408a 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -14,6 +14,9 @@ obj-$(CONFIG_RFD77402) += rfd77402.o
obj-$(CONFIG_SRF04) += srf04.o
obj-$(CONFIG_SRF08) += srf08.o
obj-$(CONFIG_SX9310) += sx9310.o
+obj-$(CONFIG_SX9324) += sx9324.o
+obj-$(CONFIG_SX9360) += sx9360.o
+obj-$(CONFIG_SX_COMMON) += sx_common.o
obj-$(CONFIG_SX9500) += sx9500.o
obj-$(CONFIG_VCNL3020) += vcnl3020.o
obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index 51f4f92ae84a..67891ce2bd09 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
+#include <linux/devm-helpers.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/irq.h>
@@ -122,7 +123,7 @@ static ssize_t as3935_sensor_sensitivity_show(struct device *dev,
return ret;
val = (val & AS3935_AFE_MASK) >> 1;
- return sprintf(buf, "%d\n", val);
+ return sysfs_emit(buf, "%d\n", val);
}
static ssize_t as3935_sensor_sensitivity_store(struct device *dev,
@@ -153,7 +154,7 @@ static ssize_t as3935_noise_level_tripped_show(struct device *dev,
int ret;
mutex_lock(&st->lock);
- ret = sprintf(buf, "%d\n", !time_after(jiffies, st->noise_tripped + HZ));
+ ret = sysfs_emit(buf, "%d\n", !time_after(jiffies, st->noise_tripped + HZ));
mutex_unlock(&st->lock);
return ret;
@@ -295,7 +296,6 @@ static void calibrate_as3935(struct as3935_state *st)
as3935_write(st, AS3935_NFLWDTH, st->nflwdth_reg);
}
-#ifdef CONFIG_PM_SLEEP
static int as3935_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -337,20 +337,7 @@ err_resume:
return ret;
}
-static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
-#define AS3935_PM_OPS (&as3935_pm_ops)
-
-#else
-#define AS3935_PM_OPS NULL
-#endif
-
-static void as3935_stop_work(void *data)
-{
- struct iio_dev *indio_dev = data;
- struct as3935_state *st = iio_priv(indio_dev);
-
- cancel_delayed_work_sync(&st->work);
-}
+static DEFINE_SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
static int as3935_probe(struct spi_device *spi)
{
@@ -432,8 +419,7 @@ static int as3935_probe(struct spi_device *spi)
calibrate_as3935(st);
- INIT_DELAYED_WORK(&st->work, as3935_event_work);
- ret = devm_add_action(dev, as3935_stop_work, indio_dev);
+ ret = devm_delayed_work_autocancel(dev, &st->work, as3935_event_work);
if (ret)
return ret;
@@ -472,7 +458,7 @@ static struct spi_driver as3935_driver = {
.driver = {
.name = "as3935",
.of_match_table = as3935_of_match,
- .pm = AS3935_PM_OPS,
+ .pm = pm_sleep_ptr(&as3935_pm_ops),
},
.probe = as3935_probe,
.id_table = as3935_id,
diff --git a/drivers/iio/proximity/ping.c b/drivers/iio/proximity/ping.c
index 1283ac1c2e03..24a97d41e115 100644
--- a/drivers/iio/proximity/ping.c
+++ b/drivers/iio/proximity/ping.c
@@ -267,8 +267,8 @@ static const struct iio_chan_spec ping_chan_spec[] = {
};
static const struct of_device_id of_ping_match[] = {
- { .compatible = "parallax,ping", .data = &pa_ping_cfg},
- { .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg},
+ { .compatible = "parallax,ping", .data = &pa_ping_cfg },
+ { .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg },
{},
};
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
index 27026c060ab9..648ae576d6fa 100644
--- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -338,7 +338,6 @@ static const struct of_device_id lidar_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, lidar_dt_ids);
-#ifdef CONFIG_PM
static int lidar_pm_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -358,18 +357,16 @@ static int lidar_pm_runtime_resume(struct device *dev)
return ret;
}
-#endif
static const struct dev_pm_ops lidar_pm_ops = {
- SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend,
- lidar_pm_runtime_resume, NULL)
+ RUNTIME_PM_OPS(lidar_pm_runtime_suspend, lidar_pm_runtime_resume, NULL)
};
static struct i2c_driver lidar_driver = {
.driver = {
.name = LIDAR_DRV_NAME,
.of_match_table = lidar_dt_ids,
- .pm = &lidar_pm_ops,
+ .pm = pm_ptr(&lidar_pm_ops),
},
.probe = lidar_probe,
.remove = lidar_remove,
diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c
index 8c06d02139b6..cb80b3c9d073 100644
--- a/drivers/iio/proximity/rfd77402.c
+++ b/drivers/iio/proximity/rfd77402.c
@@ -295,7 +295,6 @@ static int rfd77402_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int rfd77402_suspend(struct device *dev)
{
return rfd77402_powerdown(to_i2c_client(dev));
@@ -305,12 +304,12 @@ static int rfd77402_resume(struct device *dev)
{
return rfd77402_init(to_i2c_client(dev));
}
-#endif
-static SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend,
+ rfd77402_resume);
static const struct i2c_device_id rfd77402_id[] = {
- { "rfd77402", 0},
+ { "rfd77402", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rfd77402_id);
@@ -318,7 +317,7 @@ MODULE_DEVICE_TABLE(i2c, rfd77402_id);
static struct i2c_driver rfd77402_driver = {
.driver = {
.name = RFD77402_DRV_NAME,
- .pm = &rfd77402_pm_ops,
+ .pm = pm_sleep_ptr(&rfd77402_pm_ops),
},
.probe = rfd77402_probe,
.id_table = rfd77402_id,
diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c
index fe88b2bb60bc..4e6286765f01 100644
--- a/drivers/iio/proximity/srf04.c
+++ b/drivers/iio/proximity/srf04.c
@@ -235,12 +235,12 @@ static const struct iio_chan_spec srf04_chan_spec[] = {
};
static const struct of_device_id of_srf04_match[] = {
- { .compatible = "devantech,srf04", .data = &srf04_cfg},
- { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg},
- { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg},
- { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg},
- { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg},
- { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg},
+ { .compatible = "devantech,srf04", .data = &srf04_cfg },
+ { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg },
+ { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg },
+ { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg },
+ { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg },
+ { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg },
{},
};
diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c
index 9b0886760f76..ac1ab7e89d4e 100644
--- a/drivers/iio/proximity/srf08.c
+++ b/drivers/iio/proximity/srf08.c
@@ -528,9 +528,9 @@ static int srf08_probe(struct i2c_client *client,
}
static const struct of_device_id of_srf08_match[] = {
- { .compatible = "devantech,srf02", (void *)SRF02},
- { .compatible = "devantech,srf08", (void *)SRF08},
- { .compatible = "devantech,srf10", (void *)SRF10},
+ { .compatible = "devantech,srf02", (void *)SRF02 },
+ { .compatible = "devantech,srf08", (void *)SRF08 },
+ { .compatible = "devantech,srf10", (void *)SRF10 },
{},
};
diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c
index a3fdb59b06d2..ea7318b508ea 100644
--- a/drivers/iio/proximity/sx9310.c
+++ b/drivers/iio/proximity/sx9310.c
@@ -10,11 +10,10 @@
* and in January 2020 by Daniel Campello <campello@chromium.org>.
*/
-#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/irq.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/mod_devicetable.h>
@@ -22,19 +21,12 @@
#include <linux/pm.h>
#include <linux/property.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/triggered_buffer.h>
-#include <linux/iio/trigger_consumer.h>
+
+#include "sx_common.h"
/* Register definitions. */
-#define SX9310_REG_IRQ_SRC 0x00
+#define SX9310_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC
#define SX9310_REG_STAT0 0x01
#define SX9310_REG_STAT1 0x02
#define SX9310_REG_STAT1_COMPSTAT_MASK GENMASK(3, 0)
@@ -135,81 +127,36 @@
#define SX9310_WHOAMI_VALUE 0x01
#define SX9311_WHOAMI_VALUE 0x02
#define SX9310_REG_RESET 0x7f
-#define SX9310_SOFT_RESET 0xde
/* 4 hardware channels, as defined in STAT0: COMB, CS2, CS1 and CS0. */
#define SX9310_NUM_CHANNELS 4
-static_assert(SX9310_NUM_CHANNELS < BITS_PER_LONG);
-
-struct sx9310_data {
- /* Serialize access to registers and channel configuration */
- struct mutex mutex;
- struct i2c_client *client;
- struct iio_trigger *trig;
- struct regmap *regmap;
- struct regulator_bulk_data supplies[2];
- /*
- * Last reading of the proximity status for each channel.
- * We only send an event to user space when this changes.
- */
- unsigned long chan_prox_stat;
- bool trigger_enabled;
- /* Ensure correct alignment of timestamp when present. */
- struct {
- __be16 channels[SX9310_NUM_CHANNELS];
- s64 ts __aligned(8);
- } buffer;
- /* Remember enabled channels and sample rate during suspend. */
- unsigned int suspend_ctrl0;
- struct completion completion;
- unsigned long chan_read;
- unsigned long chan_event;
- unsigned int whoami;
-};
-
-static const struct iio_event_spec sx9310_events[] = {
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_RISING,
- .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_FALLING,
- .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_EITHER,
- .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
- BIT(IIO_EV_INFO_HYSTERESIS) |
- BIT(IIO_EV_INFO_VALUE),
- },
-};
-
-#define SX9310_NAMED_CHANNEL(idx, name) \
- { \
- .type = IIO_PROXIMITY, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
- .info_mask_separate_available = \
- BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
- .indexed = 1, \
- .channel = idx, \
- .extend_name = name, \
- .address = SX9310_REG_DIFF_MSB, \
- .event_spec = sx9310_events, \
- .num_event_specs = ARRAY_SIZE(sx9310_events), \
- .scan_index = idx, \
- .scan_type = { \
- .sign = 's', \
- .realbits = 12, \
- .storagebits = 16, \
- .endianness = IIO_BE, \
- }, \
- }
+static_assert(SX9310_NUM_CHANNELS <= SX_COMMON_MAX_NUM_CHANNELS);
+
+#define SX9310_NAMED_CHANNEL(idx, name) \
+{ \
+ .type = IIO_PROXIMITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_separate_available = \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = 1, \
+ .channel = idx, \
+ .extend_name = name, \
+ .address = SX9310_REG_DIFF_MSB, \
+ .event_spec = sx_common_events, \
+ .num_event_specs = ARRAY_SIZE(sx_common_events), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+}
#define SX9310_CHANNEL(idx) SX9310_NAMED_CHANNEL(idx, NULL)
static const struct iio_chan_spec sx9310_channels[] = {
@@ -251,22 +198,6 @@ static const unsigned int sx9310_scan_period_table[] = {
400, 600, 800, 1000, 2000, 3000, 4000, 5000,
};
-static ssize_t sx9310_show_samp_freq_avail(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- size_t len = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(sx9310_samp_freq_table); i++)
- len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%d ",
- sx9310_samp_freq_table[i].val,
- sx9310_samp_freq_table[i].val2);
- buf[len - 1] = '\n';
- return len;
-}
-static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sx9310_show_samp_freq_avail);
-
static const struct regmap_range sx9310_writable_reg_ranges[] = {
regmap_reg_range(SX9310_REG_IRQ_MSK, SX9310_REG_IRQ_FUNC),
regmap_reg_range(SX9310_REG_PROX_CTRL0, SX9310_REG_PROX_CTRL19),
@@ -320,64 +251,7 @@ static const struct regmap_config sx9310_regmap_config = {
.volatile_table = &sx9310_volatile_regs,
};
-static int sx9310_update_chan_en(struct sx9310_data *data,
- unsigned long chan_read,
- unsigned long chan_event)
-{
- int ret;
- unsigned long channels = chan_read | chan_event;
-
- if ((data->chan_read | data->chan_event) != channels) {
- ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL0,
- SX9310_REG_PROX_CTRL0_SENSOREN_MASK,
- channels);
- if (ret)
- return ret;
- }
- data->chan_read = chan_read;
- data->chan_event = chan_event;
- return 0;
-}
-
-static int sx9310_get_read_channel(struct sx9310_data *data, int channel)
-{
- return sx9310_update_chan_en(data, data->chan_read | BIT(channel),
- data->chan_event);
-}
-
-static int sx9310_put_read_channel(struct sx9310_data *data, int channel)
-{
- return sx9310_update_chan_en(data, data->chan_read & ~BIT(channel),
- data->chan_event);
-}
-
-static int sx9310_get_event_channel(struct sx9310_data *data, int channel)
-{
- return sx9310_update_chan_en(data, data->chan_read,
- data->chan_event | BIT(channel));
-}
-
-static int sx9310_put_event_channel(struct sx9310_data *data, int channel)
-{
- return sx9310_update_chan_en(data, data->chan_read,
- data->chan_event & ~BIT(channel));
-}
-
-static int sx9310_enable_irq(struct sx9310_data *data, unsigned int irq)
-{
- if (!data->client->irq)
- return 0;
- return regmap_update_bits(data->regmap, SX9310_REG_IRQ_MSK, irq, irq);
-}
-
-static int sx9310_disable_irq(struct sx9310_data *data, unsigned int irq)
-{
- if (!data->client->irq)
- return 0;
- return regmap_update_bits(data->regmap, SX9310_REG_IRQ_MSK, irq, 0);
-}
-
-static int sx9310_read_prox_data(struct sx9310_data *data,
+static int sx9310_read_prox_data(struct sx_common_data *data,
const struct iio_chan_spec *chan, __be16 *val)
{
int ret;
@@ -393,7 +267,7 @@ static int sx9310_read_prox_data(struct sx9310_data *data,
* If we have no interrupt support, we have to wait for a scan period
* after enabling a channel to get a result.
*/
-static int sx9310_wait_for_sample(struct sx9310_data *data)
+static int sx9310_wait_for_sample(struct sx_common_data *data)
{
int ret;
unsigned int val;
@@ -409,66 +283,7 @@ static int sx9310_wait_for_sample(struct sx9310_data *data)
return 0;
}
-static int sx9310_read_proximity(struct sx9310_data *data,
- const struct iio_chan_spec *chan, int *val)
-{
- int ret;
- __be16 rawval;
-
- mutex_lock(&data->mutex);
-
- ret = sx9310_get_read_channel(data, chan->channel);
- if (ret)
- goto out;
-
- ret = sx9310_enable_irq(data, SX9310_CONVDONE_IRQ);
- if (ret)
- goto out_put_channel;
-
- mutex_unlock(&data->mutex);
-
- if (data->client->irq) {
- ret = wait_for_completion_interruptible(&data->completion);
- reinit_completion(&data->completion);
- } else {
- ret = sx9310_wait_for_sample(data);
- }
-
- mutex_lock(&data->mutex);
-
- if (ret)
- goto out_disable_irq;
-
- ret = sx9310_read_prox_data(data, chan, &rawval);
- if (ret)
- goto out_disable_irq;
-
- *val = sign_extend32(be16_to_cpu(rawval),
- chan->address == SX9310_REG_DIFF_MSB ? 11 : 15);
-
- ret = sx9310_disable_irq(data, SX9310_CONVDONE_IRQ);
- if (ret)
- goto out_put_channel;
-
- ret = sx9310_put_read_channel(data, chan->channel);
- if (ret)
- goto out;
-
- mutex_unlock(&data->mutex);
-
- return IIO_VAL_INT;
-
-out_disable_irq:
- sx9310_disable_irq(data, SX9310_CONVDONE_IRQ);
-out_put_channel:
- sx9310_put_read_channel(data, chan->channel);
-out:
- mutex_unlock(&data->mutex);
-
- return ret;
-}
-
-static int sx9310_read_gain(struct sx9310_data *data,
+static int sx9310_read_gain(struct sx_common_data *data,
const struct iio_chan_spec *chan, int *val)
{
unsigned int regval, gain;
@@ -496,7 +311,7 @@ static int sx9310_read_gain(struct sx9310_data *data,
return IIO_VAL_INT;
}
-static int sx9310_read_samp_freq(struct sx9310_data *data, int *val, int *val2)
+static int sx9310_read_samp_freq(struct sx_common_data *data, int *val, int *val2)
{
unsigned int regval;
int ret;
@@ -516,7 +331,7 @@ static int sx9310_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2, long mask)
{
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_PROXIMITY)
@@ -528,7 +343,7 @@ static int sx9310_read_raw(struct iio_dev *indio_dev,
if (ret)
return ret;
- ret = sx9310_read_proximity(data, chan, val);
+ ret = sx_common_read_proximity(data, chan, val);
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_HARDWAREGAIN:
@@ -562,9 +377,14 @@ static int sx9310_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(sx9310_gain_vals);
*vals = sx9310_gain_vals;
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(sx9310_samp_freq_table) * 2;
+ *vals = (int *)sx9310_samp_freq_table;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
}
-
- return -EINVAL;
}
static const unsigned int sx9310_pthresh_codes[] = {
@@ -581,12 +401,12 @@ static int sx9310_get_thresh_reg(unsigned int channel)
case 1:
case 2:
return SX9310_REG_PROX_CTRL9;
+ default:
+ return -EINVAL;
}
-
- return -EINVAL;
}
-static int sx9310_read_thresh(struct sx9310_data *data,
+static int sx9310_read_thresh(struct sx_common_data *data,
const struct iio_chan_spec *chan, int *val)
{
unsigned int reg;
@@ -609,7 +429,7 @@ static int sx9310_read_thresh(struct sx9310_data *data,
return IIO_VAL_INT;
}
-static int sx9310_read_hysteresis(struct sx9310_data *data,
+static int sx9310_read_hysteresis(struct sx_common_data *data,
const struct iio_chan_spec *chan, int *val)
{
unsigned int regval, pthresh;
@@ -633,7 +453,7 @@ static int sx9310_read_hysteresis(struct sx9310_data *data,
return IIO_VAL_INT;
}
-static int sx9310_read_far_debounce(struct sx9310_data *data, int *val)
+static int sx9310_read_far_debounce(struct sx_common_data *data, int *val)
{
unsigned int regval;
int ret;
@@ -651,7 +471,7 @@ static int sx9310_read_far_debounce(struct sx9310_data *data, int *val)
return IIO_VAL_INT;
}
-static int sx9310_read_close_debounce(struct sx9310_data *data, int *val)
+static int sx9310_read_close_debounce(struct sx_common_data *data, int *val)
{
unsigned int regval;
int ret;
@@ -675,7 +495,7 @@ static int sx9310_read_event_val(struct iio_dev *indio_dev,
enum iio_event_direction dir,
enum iio_event_info info, int *val, int *val2)
{
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY)
return -EINVAL;
@@ -699,7 +519,7 @@ static int sx9310_read_event_val(struct iio_dev *indio_dev,
}
}
-static int sx9310_write_thresh(struct sx9310_data *data,
+static int sx9310_write_thresh(struct sx_common_data *data,
const struct iio_chan_spec *chan, int val)
{
unsigned int reg;
@@ -729,7 +549,7 @@ static int sx9310_write_thresh(struct sx9310_data *data,
return ret;
}
-static int sx9310_write_hysteresis(struct sx9310_data *data,
+static int sx9310_write_hysteresis(struct sx_common_data *data,
const struct iio_chan_spec *chan, int _val)
{
unsigned int hyst, val = _val;
@@ -759,7 +579,7 @@ static int sx9310_write_hysteresis(struct sx9310_data *data,
return ret;
}
-static int sx9310_write_far_debounce(struct sx9310_data *data, int val)
+static int sx9310_write_far_debounce(struct sx_common_data *data, int val)
{
int ret;
unsigned int regval;
@@ -780,7 +600,7 @@ static int sx9310_write_far_debounce(struct sx9310_data *data, int val)
return ret;
}
-static int sx9310_write_close_debounce(struct sx9310_data *data, int val)
+static int sx9310_write_close_debounce(struct sx_common_data *data, int val)
{
int ret;
unsigned int regval;
@@ -807,7 +627,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev,
enum iio_event_direction dir,
enum iio_event_info info, int val, int val2)
{
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY)
return -EINVAL;
@@ -831,7 +651,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev,
}
}
-static int sx9310_set_samp_freq(struct sx9310_data *data, int val, int val2)
+static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2)
{
int i, ret;
@@ -855,8 +675,8 @@ static int sx9310_set_samp_freq(struct sx9310_data *data, int val, int val2)
return ret;
}
-static int sx9310_write_gain(struct sx9310_data *data,
- const struct iio_chan_spec *chan, int val)
+static int sx9310_write_gain(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int val)
{
unsigned int gain, mask;
int ret;
@@ -890,7 +710,7 @@ static int sx9310_write_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int val, int val2,
long mask)
{
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY)
return -EINVAL;
@@ -900,253 +720,12 @@ static int sx9310_write_raw(struct iio_dev *indio_dev,
return sx9310_set_samp_freq(data, val, val2);
case IIO_CHAN_INFO_HARDWAREGAIN:
return sx9310_write_gain(data, chan, val);
+ default:
+ return -EINVAL;
}
-
- return -EINVAL;
-}
-
-static irqreturn_t sx9310_irq_handler(int irq, void *private)
-{
- struct iio_dev *indio_dev = private;
- struct sx9310_data *data = iio_priv(indio_dev);
-
- if (data->trigger_enabled)
- iio_trigger_poll(data->trig);
-
- /*
- * Even if no event is enabled, we need to wake the thread to clear the
- * interrupt state by reading SX9310_REG_IRQ_SRC.
- * It is not possible to do that here because regmap_read takes a mutex.
- */
- return IRQ_WAKE_THREAD;
-}
-
-static void sx9310_push_events(struct iio_dev *indio_dev)
-{
- int ret;
- unsigned int val, chan;
- struct sx9310_data *data = iio_priv(indio_dev);
- s64 timestamp = iio_get_time_ns(indio_dev);
- unsigned long prox_changed;
-
- /* Read proximity state on all channels */
- ret = regmap_read(data->regmap, SX9310_REG_STAT0, &val);
- if (ret) {
- dev_err(&data->client->dev, "i2c transfer error in irq\n");
- return;
- }
-
- /*
- * Only iterate over channels with changes on proximity status that have
- * events enabled.
- */
- prox_changed = (data->chan_prox_stat ^ val) & data->chan_event;
-
- for_each_set_bit(chan, &prox_changed, SX9310_NUM_CHANNELS) {
- int dir;
- u64 ev;
-
- dir = (val & BIT(chan)) ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
- ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
- IIO_EV_TYPE_THRESH, dir);
-
- iio_push_event(indio_dev, ev, timestamp);
- }
- data->chan_prox_stat = val;
-}
-
-static irqreturn_t sx9310_irq_thread_handler(int irq, void *private)
-{
- struct iio_dev *indio_dev = private;
- struct sx9310_data *data = iio_priv(indio_dev);
- int ret;
- unsigned int val;
-
- mutex_lock(&data->mutex);
-
- ret = regmap_read(data->regmap, SX9310_REG_IRQ_SRC, &val);
- if (ret) {
- dev_err(&data->client->dev, "i2c transfer error in irq\n");
- goto out;
- }
-
- if (val & (SX9310_FAR_IRQ | SX9310_CLOSE_IRQ))
- sx9310_push_events(indio_dev);
-
- if (val & SX9310_CONVDONE_IRQ)
- complete(&data->completion);
-
-out:
- mutex_unlock(&data->mutex);
-
- return IRQ_HANDLED;
-}
-
-static int sx9310_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 sx9310_data *data = iio_priv(indio_dev);
-
- return !!(data->chan_event & BIT(chan->channel));
-}
-
-static int sx9310_write_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir, int state)
-{
- struct sx9310_data *data = iio_priv(indio_dev);
- unsigned int eventirq = SX9310_FAR_IRQ | SX9310_CLOSE_IRQ;
- int ret;
-
- /* If the state hasn't changed, there's nothing to do. */
- if (!!(data->chan_event & BIT(chan->channel)) == state)
- return 0;
-
- mutex_lock(&data->mutex);
- if (state) {
- ret = sx9310_get_event_channel(data, chan->channel);
- if (ret)
- goto out_unlock;
- if (!(data->chan_event & ~BIT(chan->channel))) {
- ret = sx9310_enable_irq(data, eventirq);
- if (ret)
- sx9310_put_event_channel(data, chan->channel);
- }
- } else {
- ret = sx9310_put_event_channel(data, chan->channel);
- if (ret)
- goto out_unlock;
- if (!data->chan_event) {
- ret = sx9310_disable_irq(data, eventirq);
- if (ret)
- sx9310_get_event_channel(data, chan->channel);
- }
- }
-
-out_unlock:
- mutex_unlock(&data->mutex);
- return ret;
-}
-
-static struct attribute *sx9310_attributes[] = {
- &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group sx9310_attribute_group = {
- .attrs = sx9310_attributes,
-};
-
-static const struct iio_info sx9310_info = {
- .attrs = &sx9310_attribute_group,
- .read_raw = sx9310_read_raw,
- .read_avail = sx9310_read_avail,
- .read_event_value = sx9310_read_event_val,
- .write_event_value = sx9310_write_event_val,
- .write_raw = sx9310_write_raw,
- .read_event_config = sx9310_read_event_config,
- .write_event_config = sx9310_write_event_config,
-};
-
-static int sx9310_set_trigger_state(struct iio_trigger *trig, bool state)
-{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
- struct sx9310_data *data = iio_priv(indio_dev);
- int ret = 0;
-
- mutex_lock(&data->mutex);
-
- if (state)
- ret = sx9310_enable_irq(data, SX9310_CONVDONE_IRQ);
- else if (!data->chan_read)
- ret = sx9310_disable_irq(data, SX9310_CONVDONE_IRQ);
- if (ret)
- goto out;
-
- data->trigger_enabled = state;
-
-out:
- mutex_unlock(&data->mutex);
-
- return ret;
-}
-
-static const struct iio_trigger_ops sx9310_trigger_ops = {
- .set_trigger_state = sx9310_set_trigger_state,
-};
-
-static irqreturn_t sx9310_trigger_handler(int irq, void *private)
-{
- struct iio_poll_func *pf = private;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct sx9310_data *data = iio_priv(indio_dev);
- __be16 val;
- int bit, ret, i = 0;
-
- mutex_lock(&data->mutex);
-
- for_each_set_bit(bit, indio_dev->active_scan_mask,
- indio_dev->masklength) {
- ret = sx9310_read_prox_data(data, &indio_dev->channels[bit],
- &val);
- if (ret)
- goto out;
-
- data->buffer.channels[i++] = val;
- }
-
- iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
- pf->timestamp);
-
-out:
- mutex_unlock(&data->mutex);
-
- iio_trigger_notify_done(indio_dev->trig);
-
- return IRQ_HANDLED;
-}
-
-static int sx9310_buffer_preenable(struct iio_dev *indio_dev)
-{
- struct sx9310_data *data = iio_priv(indio_dev);
- unsigned long channels = 0;
- int bit, ret;
-
- mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->active_scan_mask,
- indio_dev->masklength)
- __set_bit(indio_dev->channels[bit].channel, &channels);
-
- ret = sx9310_update_chan_en(data, channels, data->chan_event);
- mutex_unlock(&data->mutex);
- return ret;
}
-static int sx9310_buffer_postdisable(struct iio_dev *indio_dev)
-{
- struct sx9310_data *data = iio_priv(indio_dev);
- int ret;
-
- mutex_lock(&data->mutex);
- ret = sx9310_update_chan_en(data, 0, data->chan_event);
- mutex_unlock(&data->mutex);
- return ret;
-}
-
-static const struct iio_buffer_setup_ops sx9310_buffer_setup_ops = {
- .preenable = sx9310_buffer_preenable,
- .postdisable = sx9310_buffer_postdisable,
-};
-
-struct sx9310_reg_default {
- u8 reg;
- u8 def;
-};
-
-static const struct sx9310_reg_default sx9310_default_regs[] = {
+static const struct sx_common_reg_default sx9310_default_regs[] = {
{ SX9310_REG_IRQ_MSK, 0x00 },
{ SX9310_REG_IRQ_FUNC, 0x00 },
/*
@@ -1191,7 +770,7 @@ static const struct sx9310_reg_default sx9310_default_regs[] = {
/* Activate all channels and perform an initial compensation. */
static int sx9310_init_compensation(struct iio_dev *indio_dev)
{
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(indio_dev);
int ret;
unsigned int val;
unsigned int ctrl0;
@@ -1209,21 +788,16 @@ static int sx9310_init_compensation(struct iio_dev *indio_dev)
ret = regmap_read_poll_timeout(data->regmap, SX9310_REG_STAT1, val,
!(val & SX9310_REG_STAT1_COMPSTAT_MASK),
20000, 2000000);
- if (ret) {
- if (ret == -ETIMEDOUT)
- dev_err(&data->client->dev,
- "initial compensation timed out: 0x%02x\n",
- val);
+ if (ret)
return ret;
- }
regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0);
return ret;
}
-static const struct sx9310_reg_default *
+static const struct sx_common_reg_default *
sx9310_get_default_reg(struct device *dev, int idx,
- struct sx9310_reg_default *reg_def)
+ struct sx_common_reg_default *reg_def)
{
u32 combined[SX9310_NUM_CHANNELS];
u32 start = 0, raw = 0, pos = 0;
@@ -1324,47 +898,21 @@ sx9310_get_default_reg(struct device *dev, int idx,
return reg_def;
}
-static int sx9310_init_device(struct iio_dev *indio_dev)
+static int sx9310_check_whoami(struct device *dev,
+ struct iio_dev *indio_dev)
{
- struct sx9310_data *data = iio_priv(indio_dev);
- struct sx9310_reg_default tmp;
- const struct sx9310_reg_default *initval;
+ struct sx_common_data *data = iio_priv(indio_dev);
+ unsigned int long ddata;
+ unsigned int whoami;
int ret;
- unsigned int i, val;
-
- ret = regmap_write(data->regmap, SX9310_REG_RESET, SX9310_SOFT_RESET);
- if (ret)
- return ret;
-
- usleep_range(1000, 2000); /* power-up time is ~1ms. */
- /* Clear reset interrupt state by reading SX9310_REG_IRQ_SRC. */
- ret = regmap_read(data->regmap, SX9310_REG_IRQ_SRC, &val);
+ ret = regmap_read(data->regmap, SX9310_REG_WHOAMI, &whoami);
if (ret)
return ret;
- /* Program some sane defaults. */
- for (i = 0; i < ARRAY_SIZE(sx9310_default_regs); i++) {
- initval = sx9310_get_default_reg(&indio_dev->dev, i, &tmp);
- ret = regmap_write(data->regmap, initval->reg, initval->def);
- if (ret)
- return ret;
- }
-
- return sx9310_init_compensation(indio_dev);
-}
-
-static int sx9310_set_indio_dev_name(struct device *dev,
- struct iio_dev *indio_dev,
- unsigned int whoami)
-{
- unsigned int long ddata;
-
ddata = (uintptr_t)device_get_match_data(dev);
- if (ddata != whoami) {
- dev_err(dev, "WHOAMI does not match device data: %u\n", whoami);
- return -ENODEV;
- }
+ if (ddata != whoami)
+ return -EINVAL;
switch (whoami) {
case SX9310_WHOAMI_VALUE:
@@ -1374,115 +922,52 @@ static int sx9310_set_indio_dev_name(struct device *dev,
indio_dev->name = "sx9311";
break;
default:
- dev_err(dev, "unexpected WHOAMI response: %u\n", whoami);
return -ENODEV;
}
return 0;
}
-static void sx9310_regulator_disable(void *_data)
-{
- struct sx9310_data *data = _data;
+static const struct sx_common_chip_info sx9310_chip_info = {
+ .reg_stat = SX9310_REG_STAT0,
+ .reg_irq_msk = SX9310_REG_IRQ_MSK,
+ .reg_enable_chan = SX9310_REG_PROX_CTRL0,
+ .reg_reset = SX9310_REG_RESET,
+
+ .mask_enable_chan = SX9310_REG_STAT1_COMPSTAT_MASK,
+ .irq_msk_offset = 3,
+ .num_channels = SX9310_NUM_CHANNELS,
+ .num_default_regs = ARRAY_SIZE(sx9310_default_regs),
+
+ .ops = {
+ .read_prox_data = sx9310_read_prox_data,
+ .check_whoami = sx9310_check_whoami,
+ .init_compensation = sx9310_init_compensation,
+ .wait_for_sample = sx9310_wait_for_sample,
+ .get_default_reg = sx9310_get_default_reg,
+ },
- regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies);
-}
+ .iio_channels = sx9310_channels,
+ .num_iio_channels = ARRAY_SIZE(sx9310_channels),
+ .iio_info = {
+ .read_raw = sx9310_read_raw,
+ .read_avail = sx9310_read_avail,
+ .read_event_value = sx9310_read_event_val,
+ .write_event_value = sx9310_write_event_val,
+ .write_raw = sx9310_write_raw,
+ .read_event_config = sx_common_read_event_config,
+ .write_event_config = sx_common_write_event_config,
+ },
+};
static int sx9310_probe(struct i2c_client *client)
{
- int ret;
- struct device *dev = &client->dev;
- struct iio_dev *indio_dev;
- struct sx9310_data *data;
-
- indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
- if (!indio_dev)
- return -ENOMEM;
-
- data = iio_priv(indio_dev);
- data->client = client;
- data->supplies[0].supply = "vdd";
- data->supplies[1].supply = "svdd";
- mutex_init(&data->mutex);
- init_completion(&data->completion);
-
- data->regmap = devm_regmap_init_i2c(client, &sx9310_regmap_config);
- if (IS_ERR(data->regmap))
- return PTR_ERR(data->regmap);
-
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
- data->supplies);
- if (ret)
- return ret;
-
- ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
- if (ret)
- return ret;
- /* Must wait for Tpor time after initial power up */
- usleep_range(1000, 1100);
-
- ret = devm_add_action_or_reset(dev, sx9310_regulator_disable, data);
- if (ret)
- return ret;
-
- ret = regmap_read(data->regmap, SX9310_REG_WHOAMI, &data->whoami);
- if (ret) {
- dev_err(dev, "error in reading WHOAMI register: %d", ret);
- return ret;
- }
-
- ret = sx9310_set_indio_dev_name(dev, indio_dev, data->whoami);
- if (ret)
- return ret;
-
- ACPI_COMPANION_SET(&indio_dev->dev, ACPI_COMPANION(dev));
- indio_dev->channels = sx9310_channels;
- indio_dev->num_channels = ARRAY_SIZE(sx9310_channels);
- indio_dev->info = &sx9310_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- i2c_set_clientdata(client, indio_dev);
-
- ret = sx9310_init_device(indio_dev);
- if (ret)
- return ret;
-
- if (client->irq) {
- ret = devm_request_threaded_irq(dev, client->irq,
- sx9310_irq_handler,
- sx9310_irq_thread_handler,
- IRQF_ONESHOT,
- "sx9310_event", indio_dev);
- if (ret)
- return ret;
-
- data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
- indio_dev->name,
- iio_device_id(indio_dev));
- if (!data->trig)
- return -ENOMEM;
-
- data->trig->ops = &sx9310_trigger_ops;
- iio_trigger_set_drvdata(data->trig, indio_dev);
-
- ret = devm_iio_trigger_register(dev, data->trig);
- if (ret)
- return ret;
- }
-
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- iio_pollfunc_store_time,
- sx9310_trigger_handler,
- &sx9310_buffer_setup_ops);
- if (ret)
- return ret;
-
- return devm_iio_device_register(dev, indio_dev);
+ return sx_common_probe(client, &sx9310_chip_info, &sx9310_regmap_config);
}
static int __maybe_unused sx9310_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
u8 ctrl0;
int ret;
@@ -1490,11 +975,11 @@ static int __maybe_unused sx9310_suspend(struct device *dev)
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0,
- &data->suspend_ctrl0);
+ &data->suspend_ctrl);
if (ret)
goto out;
- ctrl0 = data->suspend_ctrl0 & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK;
+ ctrl0 = data->suspend_ctrl & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK;
ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0);
if (ret)
goto out;
@@ -1508,8 +993,7 @@ out:
static int __maybe_unused sx9310_resume(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
- struct sx9310_data *data = iio_priv(indio_dev);
+ struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
mutex_lock(&data->mutex);
@@ -1518,7 +1002,7 @@ static int __maybe_unused sx9310_resume(struct device *dev)
goto out;
ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0,
- data->suspend_ctrl0);
+ data->suspend_ctrl);
out:
mutex_unlock(&data->mutex);
@@ -1529,9 +1013,7 @@ out:
return 0;
}
-static const struct dev_pm_ops sx9310_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sx9310_suspend, sx9310_resume)
-};
+static SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume);
static const struct acpi_device_id sx9310_acpi_match[] = {
{ "STH9310", SX9310_WHOAMI_VALUE },
@@ -1577,3 +1059,4 @@ MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
MODULE_AUTHOR("Daniel Campello <campello@chromium.org>");
MODULE_DESCRIPTION("Driver for Semtech SX9310/SX9311 proximity sensor");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(SEMTECH_PROX);
diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c
new file mode 100644
index 000000000000..0d9bbbb50cb4
--- /dev/null
+++ b/drivers/iio/proximity/sx9324.c
@@ -0,0 +1,1068 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Driver for Semtech's SX9324 capacitive proximity/button solution.
+ * Based on SX9324 driver and copy of datasheet at:
+ * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+
+#include "sx_common.h"
+
+/* Register definitions. */
+#define SX9324_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC
+#define SX9324_REG_STAT0 0x01
+#define SX9324_REG_STAT1 0x02
+#define SX9324_REG_STAT2 0x03
+#define SX9324_REG_STAT2_COMPSTAT_MASK GENMASK(3, 0)
+#define SX9324_REG_STAT3 0x04
+#define SX9324_REG_IRQ_MSK 0x05
+#define SX9324_CONVDONE_IRQ BIT(3)
+#define SX9324_FAR_IRQ BIT(5)
+#define SX9324_CLOSE_IRQ BIT(6)
+#define SX9324_REG_IRQ_CFG0 0x06
+#define SX9324_REG_IRQ_CFG1 0x07
+#define SX9324_REG_IRQ_CFG1_FAILCOND 0x80
+#define SX9324_REG_IRQ_CFG2 0x08
+
+#define SX9324_REG_GNRL_CTRL0 0x10
+#define SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK GENMASK(4, 0)
+#define SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS 0x16
+#define SX9324_REG_GNRL_CTRL1 0x11
+#define SX9324_REG_GNRL_CTRL1_PHEN_MASK GENMASK(3, 0)
+#define SX9324_REG_GNRL_CTRL1_PAUSECTRL 0x20
+
+#define SX9324_REG_I2C_ADDR 0x14
+#define SX9324_REG_CLK_SPRD 0x15
+
+#define SX9324_REG_AFE_CTRL0 0x20
+#define SX9324_REG_AFE_CTRL1 0x21
+#define SX9324_REG_AFE_CTRL2 0x22
+#define SX9324_REG_AFE_CTRL3 0x23
+#define SX9324_REG_AFE_CTRL4 0x24
+#define SX9324_REG_AFE_CTRL4_FREQ_83_33HZ 0x40
+#define SX9324_REG_AFE_CTRL4_RESOLUTION_MASK GENMASK(2, 0)
+#define SX9324_REG_AFE_CTRL4_RES_100 0x04
+#define SX9324_REG_AFE_CTRL5 0x25
+#define SX9324_REG_AFE_CTRL6 0x26
+#define SX9324_REG_AFE_CTRL7 0x27
+#define SX9324_REG_AFE_PH0 0x28
+#define SX9324_REG_AFE_PH0_PIN_MASK(_pin) \
+ GENMASK(2 * (_pin) + 1, 2 * (_pin))
+
+#define SX9324_REG_AFE_PH1 0x29
+#define SX9324_REG_AFE_PH2 0x2a
+#define SX9324_REG_AFE_PH3 0x2b
+#define SX9324_REG_AFE_CTRL8 0x2c
+#define SX9324_REG_AFE_CTRL8_RESFILTN_4KOHM 0x02
+#define SX9324_REG_AFE_CTRL9 0x2d
+#define SX9324_REG_AFE_CTRL9_AGAIN_1 0x08
+
+#define SX9324_REG_PROX_CTRL0 0x30
+#define SX9324_REG_PROX_CTRL0_GAIN_MASK GENMASK(5, 3)
+#define SX9324_REG_PROX_CTRL0_GAIN_1 0x80
+#define SX9324_REG_PROX_CTRL0_RAWFILT_MASK GENMASK(2, 0)
+#define SX9324_REG_PROX_CTRL0_RAWFILT_1P50 0x01
+#define SX9324_REG_PROX_CTRL1 0x31
+#define SX9324_REG_PROX_CTRL2 0x32
+#define SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K 0x20
+#define SX9324_REG_PROX_CTRL3 0x33
+#define SX9324_REG_PROX_CTRL3_AVGDEB_2SAMPLES 0x40
+#define SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K 0x20
+#define SX9324_REG_PROX_CTRL4 0x34
+#define SX9324_REG_PROX_CTRL4_AVGNEGFILT_MASK GENMASK(5, 3)
+#define SX9324_REG_PROX_CTRL4_AVGNEG_FILT_2 0x08
+#define SX9324_REG_PROX_CTRL4_AVGPOSFILT_MASK GENMASK(2, 0)
+#define SX9324_REG_PROX_CTRL3_AVGPOS_FILT_256 0x04
+#define SX9324_REG_PROX_CTRL5 0x35
+#define SX9324_REG_PROX_CTRL5_HYST_MASK GENMASK(5, 4)
+#define SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK GENMASK(3, 2)
+#define SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK GENMASK(1, 0)
+#define SX9324_REG_PROX_CTRL6 0x36
+#define SX9324_REG_PROX_CTRL6_PROXTHRESH_32 0x08
+#define SX9324_REG_PROX_CTRL7 0x37
+
+#define SX9324_REG_ADV_CTRL0 0x40
+#define SX9324_REG_ADV_CTRL1 0x41
+#define SX9324_REG_ADV_CTRL2 0x42
+#define SX9324_REG_ADV_CTRL3 0x43
+#define SX9324_REG_ADV_CTRL4 0x44
+#define SX9324_REG_ADV_CTRL5 0x45
+#define SX9324_REG_ADV_CTRL5_STARTUPSENS_MASK GENMASK(3, 2)
+#define SX9324_REG_ADV_CTRL5_STARTUP_SENSOR_1 0x04
+#define SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1 0x01
+#define SX9324_REG_ADV_CTRL6 0x46
+#define SX9324_REG_ADV_CTRL7 0x47
+#define SX9324_REG_ADV_CTRL8 0x48
+#define SX9324_REG_ADV_CTRL9 0x49
+#define SX9324_REG_ADV_CTRL10 0x4a
+#define SX9324_REG_ADV_CTRL11 0x4b
+#define SX9324_REG_ADV_CTRL12 0x4c
+#define SX9324_REG_ADV_CTRL13 0x4d
+#define SX9324_REG_ADV_CTRL14 0x4e
+#define SX9324_REG_ADV_CTRL15 0x4f
+#define SX9324_REG_ADV_CTRL16 0x50
+#define SX9324_REG_ADV_CTRL17 0x51
+#define SX9324_REG_ADV_CTRL18 0x52
+#define SX9324_REG_ADV_CTRL19 0x53
+#define SX9324_REG_ADV_CTRL20 0x54
+#define SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION 0xf0
+
+#define SX9324_REG_PHASE_SEL 0x60
+
+#define SX9324_REG_USEFUL_MSB 0x61
+#define SX9324_REG_USEFUL_LSB 0x62
+
+#define SX9324_REG_AVG_MSB 0x63
+#define SX9324_REG_AVG_LSB 0x64
+
+#define SX9324_REG_DIFF_MSB 0x65
+#define SX9324_REG_DIFF_LSB 0x66
+
+#define SX9324_REG_OFFSET_MSB 0x67
+#define SX9324_REG_OFFSET_LSB 0x68
+
+#define SX9324_REG_SAR_MSB 0x69
+#define SX9324_REG_SAR_LSB 0x6a
+
+#define SX9324_REG_RESET 0x9f
+/* Write this to REG_RESET to do a soft reset. */
+#define SX9324_SOFT_RESET 0xde
+
+#define SX9324_REG_WHOAMI 0xfa
+#define SX9324_WHOAMI_VALUE 0x23
+
+#define SX9324_REG_REVISION 0xfe
+
+/* 4 channels, as defined in STAT0: PH0, PH1, PH2 and PH3. */
+#define SX9324_NUM_CHANNELS 4
+/* 3 CS pins: CS0, CS1, CS2. */
+#define SX9324_NUM_PINS 3
+
+static const char * const sx9324_cs_pin_usage[] = { "HZ", "MI", "DS", "GD" };
+
+static ssize_t sx9324_phase_configuration_show(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ unsigned int val;
+ int i, ret, pin_idx;
+ size_t len = 0;
+
+ ret = regmap_read(data->regmap, SX9324_REG_AFE_PH0 + chan->channel, &val);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < SX9324_NUM_PINS; i++) {
+ pin_idx = (val & SX9324_REG_AFE_PH0_PIN_MASK(i)) >> (2 * i);
+ len += sysfs_emit_at(buf, len, "%s,",
+ sx9324_cs_pin_usage[pin_idx]);
+ }
+ buf[len - 1] = '\n';
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info sx9324_channel_ext_info[] = {
+ {
+ .name = "setup",
+ .shared = IIO_SEPARATE,
+ .read = sx9324_phase_configuration_show,
+ },
+ {}
+};
+
+#define SX9324_CHANNEL(idx) \
+{ \
+ .type = IIO_PROXIMITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_separate_available = \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = 1, \
+ .channel = idx, \
+ .address = SX9324_REG_DIFF_MSB, \
+ .event_spec = sx_common_events, \
+ .num_event_specs = ARRAY_SIZE(sx_common_events), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = sx9324_channel_ext_info, \
+}
+
+static const struct iio_chan_spec sx9324_channels[] = {
+ SX9324_CHANNEL(0), /* Phase 0 */
+ SX9324_CHANNEL(1), /* Phase 1 */
+ SX9324_CHANNEL(2), /* Phase 2 */
+ SX9324_CHANNEL(3), /* Phase 3 */
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+/*
+ * Each entry contains the integer part (val) and the fractional part, in micro
+ * seconds. It conforms to the IIO output IIO_VAL_INT_PLUS_MICRO.
+ */
+static const struct {
+ int val;
+ int val2;
+} sx9324_samp_freq_table[] = {
+ { 1000, 0 }, /* 00000: Min (no idle time) */
+ { 500, 0 }, /* 00001: 2 ms */
+ { 250, 0 }, /* 00010: 4 ms */
+ { 166, 666666 }, /* 00011: 6 ms */
+ { 125, 0 }, /* 00100: 8 ms */
+ { 100, 0 }, /* 00101: 10 ms */
+ { 71, 428571 }, /* 00110: 14 ms */
+ { 55, 555556 }, /* 00111: 18 ms */
+ { 45, 454545 }, /* 01000: 22 ms */
+ { 38, 461538 }, /* 01001: 26 ms */
+ { 33, 333333 }, /* 01010: 30 ms */
+ { 29, 411765 }, /* 01011: 34 ms */
+ { 26, 315789 }, /* 01100: 38 ms */
+ { 23, 809524 }, /* 01101: 42 ms */
+ { 21, 739130 }, /* 01110: 46 ms */
+ { 20, 0 }, /* 01111: 50 ms */
+ { 17, 857143 }, /* 10000: 56 ms */
+ { 16, 129032 }, /* 10001: 62 ms */
+ { 14, 705882 }, /* 10010: 68 ms */
+ { 13, 513514 }, /* 10011: 74 ms */
+ { 12, 500000 }, /* 10100: 80 ms */
+ { 11, 111111 }, /* 10101: 90 ms */
+ { 10, 0 }, /* 10110: 100 ms (Typ.) */
+ { 5, 0 }, /* 10111: 200 ms */
+ { 3, 333333 }, /* 11000: 300 ms */
+ { 2, 500000 }, /* 11001: 400 ms */
+ { 1, 666667 }, /* 11010: 600 ms */
+ { 1, 250000 }, /* 11011: 800 ms */
+ { 1, 0 }, /* 11100: 1 s */
+ { 0, 500000 }, /* 11101: 2 s */
+ { 0, 333333 }, /* 11110: 3 s */
+ { 0, 250000 }, /* 11111: 4 s */
+};
+
+static const unsigned int sx9324_scan_period_table[] = {
+ 2, 15, 30, 45, 60, 90, 120, 200,
+ 400, 600, 800, 1000, 2000, 3000, 4000, 5000,
+};
+
+static const struct regmap_range sx9324_writable_reg_ranges[] = {
+ /*
+ * To set COMPSTAT for compensation, even if datasheet says register is
+ * RO.
+ */
+ regmap_reg_range(SX9324_REG_STAT2, SX9324_REG_STAT2),
+ regmap_reg_range(SX9324_REG_IRQ_MSK, SX9324_REG_IRQ_CFG2),
+ regmap_reg_range(SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL1),
+ /* Leave i2c and clock spreading as unavailable */
+ regmap_reg_range(SX9324_REG_AFE_CTRL0, SX9324_REG_AFE_CTRL9),
+ regmap_reg_range(SX9324_REG_PROX_CTRL0, SX9324_REG_PROX_CTRL7),
+ regmap_reg_range(SX9324_REG_ADV_CTRL0, SX9324_REG_ADV_CTRL20),
+ regmap_reg_range(SX9324_REG_PHASE_SEL, SX9324_REG_PHASE_SEL),
+ regmap_reg_range(SX9324_REG_OFFSET_MSB, SX9324_REG_OFFSET_LSB),
+ regmap_reg_range(SX9324_REG_RESET, SX9324_REG_RESET),
+};
+
+static const struct regmap_access_table sx9324_writeable_regs = {
+ .yes_ranges = sx9324_writable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sx9324_writable_reg_ranges),
+};
+
+/*
+ * All allocated registers are readable, so we just list unallocated
+ * ones.
+ */
+static const struct regmap_range sx9324_non_readable_reg_ranges[] = {
+ regmap_reg_range(SX9324_REG_IRQ_CFG2 + 1, SX9324_REG_GNRL_CTRL0 - 1),
+ regmap_reg_range(SX9324_REG_GNRL_CTRL1 + 1, SX9324_REG_AFE_CTRL0 - 1),
+ regmap_reg_range(SX9324_REG_AFE_CTRL9 + 1, SX9324_REG_PROX_CTRL0 - 1),
+ regmap_reg_range(SX9324_REG_PROX_CTRL7 + 1, SX9324_REG_ADV_CTRL0 - 1),
+ regmap_reg_range(SX9324_REG_ADV_CTRL20 + 1, SX9324_REG_PHASE_SEL - 1),
+ regmap_reg_range(SX9324_REG_SAR_LSB + 1, SX9324_REG_RESET - 1),
+ regmap_reg_range(SX9324_REG_RESET + 1, SX9324_REG_WHOAMI - 1),
+ regmap_reg_range(SX9324_REG_WHOAMI + 1, SX9324_REG_REVISION - 1),
+};
+
+static const struct regmap_access_table sx9324_readable_regs = {
+ .no_ranges = sx9324_non_readable_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(sx9324_non_readable_reg_ranges),
+};
+
+static const struct regmap_range sx9324_volatile_reg_ranges[] = {
+ regmap_reg_range(SX9324_REG_IRQ_SRC, SX9324_REG_STAT3),
+ regmap_reg_range(SX9324_REG_USEFUL_MSB, SX9324_REG_DIFF_LSB),
+ regmap_reg_range(SX9324_REG_SAR_MSB, SX9324_REG_SAR_LSB),
+ regmap_reg_range(SX9324_REG_WHOAMI, SX9324_REG_WHOAMI),
+ regmap_reg_range(SX9324_REG_REVISION, SX9324_REG_REVISION),
+};
+
+static const struct regmap_access_table sx9324_volatile_regs = {
+ .yes_ranges = sx9324_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sx9324_volatile_reg_ranges),
+};
+
+static const struct regmap_config sx9324_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SX9324_REG_REVISION,
+ .cache_type = REGCACHE_RBTREE,
+
+ .wr_table = &sx9324_writeable_regs,
+ .rd_table = &sx9324_readable_regs,
+ .volatile_table = &sx9324_volatile_regs,
+};
+
+static int sx9324_read_prox_data(struct sx_common_data *data,
+ const struct iio_chan_spec *chan,
+ __be16 *val)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, SX9324_REG_PHASE_SEL, chan->channel);
+ if (ret < 0)
+ return ret;
+
+ return regmap_bulk_read(data->regmap, chan->address, val, sizeof(*val));
+}
+
+/*
+ * If we have no interrupt support, we have to wait for a scan period
+ * after enabling a channel to get a result.
+ */
+static int sx9324_wait_for_sample(struct sx_common_data *data)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL0, &val);
+ if (ret < 0)
+ return ret;
+ val = FIELD_GET(SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, val);
+
+ msleep(sx9324_scan_period_table[val]);
+
+ return 0;
+}
+
+static int sx9324_read_gain(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int *val)
+{
+ unsigned int reg, regval;
+ int ret;
+
+ reg = SX9324_REG_PROX_CTRL0 + chan->channel / 2;
+ ret = regmap_read(data->regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ *val = 1 << FIELD_GET(SX9324_REG_PROX_CTRL0_GAIN_MASK, regval);
+
+ return IIO_VAL_INT;
+}
+
+static int sx9324_read_samp_freq(struct sx_common_data *data,
+ int *val, int *val2)
+{
+ int ret;
+ unsigned int regval;
+
+ ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL0, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, regval);
+ *val = sx9324_samp_freq_table[regval].val;
+ *val2 = sx9324_samp_freq_table[regval].val2;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int sx9324_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = sx_common_read_proximity(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = sx9324_read_gain(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return sx9324_read_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const int sx9324_gain_vals[] = { 1, 2, 4, 8 };
+
+static int sx9324_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ if (chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(sx9324_gain_vals);
+ *vals = sx9324_gain_vals;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(sx9324_samp_freq_table) * 2;
+ *vals = (int *)sx9324_samp_freq_table;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9324_set_samp_freq(struct sx_common_data *data,
+ int val, int val2)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(sx9324_samp_freq_table); i++)
+ if (val == sx9324_samp_freq_table[i].val &&
+ val2 == sx9324_samp_freq_table[i].val2)
+ break;
+
+ if (i == ARRAY_SIZE(sx9324_samp_freq_table))
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap,
+ SX9324_REG_GNRL_CTRL0,
+ SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, i);
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9324_read_thresh(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int *val)
+{
+ unsigned int regval;
+ unsigned int reg;
+ int ret;
+
+ /*
+ * TODO(gwendal): Depending on the phase function
+ * (proximity/table/body), retrieve the right threshold.
+ * For now, return the proximity threshold.
+ */
+ reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2;
+ ret = regmap_read(data->regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ if (regval <= 1)
+ *val = regval;
+ else
+ *val = (regval * regval) / 2;
+
+ return IIO_VAL_INT;
+}
+
+static int sx9324_read_hysteresis(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int *val)
+{
+ unsigned int regval, pthresh;
+ int ret;
+
+ ret = sx9324_read_thresh(data, chan, &pthresh);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(data->regmap, SX9324_REG_PROX_CTRL5, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9324_REG_PROX_CTRL5_HYST_MASK, regval);
+ if (!regval)
+ *val = 0;
+ else
+ *val = pthresh >> (5 - regval);
+
+ return IIO_VAL_INT;
+}
+
+static int sx9324_read_far_debounce(struct sx_common_data *data, int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, SX9324_REG_PROX_CTRL5, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, regval);
+ if (regval)
+ *val = 1 << regval;
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+}
+
+static int sx9324_read_close_debounce(struct sx_common_data *data, int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, SX9324_REG_PROX_CTRL5, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, regval);
+ if (regval)
+ *val = 1 << regval;
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+}
+
+static int sx9324_read_event_val(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 sx_common_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return sx9324_read_thresh(data, chan, val);
+ case IIO_EV_INFO_PERIOD:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return sx9324_read_far_debounce(data, val);
+ case IIO_EV_DIR_FALLING:
+ return sx9324_read_close_debounce(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_HYSTERESIS:
+ return sx9324_read_hysteresis(data, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9324_write_thresh(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int _val)
+{
+ unsigned int reg, val = _val;
+ int ret;
+
+ reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2;
+
+ if (val >= 1)
+ val = int_sqrt(2 * val);
+
+ if (val > 0xff)
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_write(data->regmap, reg, val);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9324_write_hysteresis(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int _val)
+{
+ unsigned int hyst, val = _val;
+ int ret, pthresh;
+
+ ret = sx9324_read_thresh(data, chan, &pthresh);
+ if (ret < 0)
+ return ret;
+
+ if (val == 0)
+ hyst = 0;
+ else if (val >= pthresh >> 2)
+ hyst = 3;
+ else if (val >= pthresh >> 3)
+ hyst = 2;
+ else if (val >= pthresh >> 4)
+ hyst = 1;
+ else
+ return -EINVAL;
+
+ hyst = FIELD_PREP(SX9324_REG_PROX_CTRL5_HYST_MASK, hyst);
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5,
+ SX9324_REG_PROX_CTRL5_HYST_MASK, hyst);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9324_write_far_debounce(struct sx_common_data *data, int _val)
+{
+ unsigned int regval, val = _val;
+ int ret;
+
+ if (val > 0)
+ val = ilog2(val);
+ if (!FIELD_FIT(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, val))
+ return -EINVAL;
+
+ regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, val);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5,
+ SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK,
+ regval);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9324_write_close_debounce(struct sx_common_data *data, int _val)
+{
+ unsigned int regval, val = _val;
+ int ret;
+
+ if (val > 0)
+ val = ilog2(val);
+ if (!FIELD_FIT(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val))
+ return -EINVAL;
+
+ regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5,
+ SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK,
+ regval);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9324_write_event_val(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 sx_common_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return sx9324_write_thresh(data, chan, val);
+ case IIO_EV_INFO_PERIOD:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return sx9324_write_far_debounce(data, val);
+ case IIO_EV_DIR_FALLING:
+ return sx9324_write_close_debounce(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_HYSTERESIS:
+ return sx9324_write_hysteresis(data, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9324_write_gain(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int val)
+{
+ unsigned int gain, reg;
+ int ret;
+
+ gain = ilog2(val);
+ reg = SX9324_REG_PROX_CTRL0 + chan->channel / 2;
+ gain = FIELD_PREP(SX9324_REG_PROX_CTRL0_GAIN_MASK, gain);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, reg,
+ SX9324_REG_PROX_CTRL0_GAIN_MASK,
+ gain);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9324_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2,
+ long mask)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return sx9324_set_samp_freq(data, val, val2);
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ return sx9324_write_gain(data, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct sx_common_reg_default sx9324_default_regs[] = {
+ { SX9324_REG_IRQ_MSK, 0x00 },
+ { SX9324_REG_IRQ_CFG0, 0x00 },
+ { SX9324_REG_IRQ_CFG1, SX9324_REG_IRQ_CFG1_FAILCOND },
+ { SX9324_REG_IRQ_CFG2, 0x00 },
+ { SX9324_REG_GNRL_CTRL0, SX9324_REG_GNRL_CTRL0_SCANPERIOD_100MS },
+ /*
+ * The lower 4 bits should not be set as it enable sensors measurements.
+ * Turning the detection on before the configuration values are set to
+ * good values can cause the device to return erroneous readings.
+ */
+ { SX9324_REG_GNRL_CTRL1, SX9324_REG_GNRL_CTRL1_PAUSECTRL },
+
+ { SX9324_REG_AFE_CTRL0, 0x00 },
+ { SX9324_REG_AFE_CTRL3, 0x00 },
+ { SX9324_REG_AFE_CTRL4, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ |
+ SX9324_REG_AFE_CTRL4_RES_100 },
+ { SX9324_REG_AFE_CTRL6, 0x00 },
+ { SX9324_REG_AFE_CTRL7, SX9324_REG_AFE_CTRL4_FREQ_83_33HZ |
+ SX9324_REG_AFE_CTRL4_RES_100 },
+
+ /* TODO(gwendal): PHx use chip default or all grounded? */
+ { SX9324_REG_AFE_PH0, 0x29 },
+ { SX9324_REG_AFE_PH1, 0x26 },
+ { SX9324_REG_AFE_PH2, 0x1a },
+ { SX9324_REG_AFE_PH3, 0x16 },
+
+ { SX9324_REG_AFE_CTRL8, SX9324_REG_AFE_CTRL8_RESFILTN_4KOHM },
+ { SX9324_REG_AFE_CTRL9, SX9324_REG_AFE_CTRL9_AGAIN_1 },
+
+ { SX9324_REG_PROX_CTRL0, SX9324_REG_PROX_CTRL0_GAIN_1 |
+ SX9324_REG_PROX_CTRL0_RAWFILT_1P50 },
+ { SX9324_REG_PROX_CTRL1, SX9324_REG_PROX_CTRL0_GAIN_1 |
+ SX9324_REG_PROX_CTRL0_RAWFILT_1P50 },
+ { SX9324_REG_PROX_CTRL2, SX9324_REG_PROX_CTRL2_AVGNEG_THRESH_16K },
+ { SX9324_REG_PROX_CTRL3, SX9324_REG_PROX_CTRL3_AVGDEB_2SAMPLES |
+ SX9324_REG_PROX_CTRL3_AVGPOS_THRESH_16K },
+ { SX9324_REG_PROX_CTRL4, SX9324_REG_PROX_CTRL4_AVGNEG_FILT_2 |
+ SX9324_REG_PROX_CTRL3_AVGPOS_FILT_256 },
+ { SX9324_REG_PROX_CTRL5, 0x00 },
+ { SX9324_REG_PROX_CTRL6, SX9324_REG_PROX_CTRL6_PROXTHRESH_32 },
+ { SX9324_REG_PROX_CTRL7, SX9324_REG_PROX_CTRL6_PROXTHRESH_32 },
+ { SX9324_REG_ADV_CTRL0, 0x00 },
+ { SX9324_REG_ADV_CTRL1, 0x00 },
+ { SX9324_REG_ADV_CTRL2, 0x00 },
+ { SX9324_REG_ADV_CTRL3, 0x00 },
+ { SX9324_REG_ADV_CTRL4, 0x00 },
+ { SX9324_REG_ADV_CTRL5, SX9324_REG_ADV_CTRL5_STARTUP_SENSOR_1 |
+ SX9324_REG_ADV_CTRL5_STARTUP_METHOD_1 },
+ { SX9324_REG_ADV_CTRL6, 0x00 },
+ { SX9324_REG_ADV_CTRL7, 0x00 },
+ { SX9324_REG_ADV_CTRL8, 0x00 },
+ { SX9324_REG_ADV_CTRL9, 0x00 },
+ /* Body/Table threshold */
+ { SX9324_REG_ADV_CTRL10, 0x00 },
+ { SX9324_REG_ADV_CTRL11, 0x00 },
+ { SX9324_REG_ADV_CTRL12, 0x00 },
+ /* TODO(gwendal): SAR currenly disabled */
+ { SX9324_REG_ADV_CTRL13, 0x00 },
+ { SX9324_REG_ADV_CTRL14, 0x00 },
+ { SX9324_REG_ADV_CTRL15, 0x00 },
+ { SX9324_REG_ADV_CTRL16, 0x00 },
+ { SX9324_REG_ADV_CTRL17, 0x00 },
+ { SX9324_REG_ADV_CTRL18, 0x00 },
+ { SX9324_REG_ADV_CTRL19, SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION },
+ { SX9324_REG_ADV_CTRL20, SX9324_REG_ADV_CTRL19_HIGHT_FAILURE_THRESH_SATURATION },
+};
+
+/* Activate all channels and perform an initial compensation. */
+static int sx9324_init_compensation(struct iio_dev *indio_dev)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ /* run the compensation phase on all channels */
+ ret = regmap_update_bits(data->regmap, SX9324_REG_STAT2,
+ SX9324_REG_STAT2_COMPSTAT_MASK,
+ SX9324_REG_STAT2_COMPSTAT_MASK);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(data->regmap, SX9324_REG_STAT2, val,
+ !(val & SX9324_REG_STAT2_COMPSTAT_MASK),
+ 20000, 2000000);
+}
+
+static const struct sx_common_reg_default *
+sx9324_get_default_reg(struct device *dev, int idx,
+ struct sx_common_reg_default *reg_def)
+{
+#define SX9324_PIN_DEF "semtech,ph0-pin"
+#define SX9324_RESOLUTION_DEF "semtech,ph01-resolution"
+#define SX9324_PROXRAW_DEF "semtech,ph01-proxraw-strength"
+ unsigned int pin_defs[SX9324_NUM_PINS];
+ char prop[] = SX9324_PROXRAW_DEF;
+ u32 start = 0, raw = 0, pos = 0;
+ int ret, count, ph, pin;
+
+ memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def));
+ switch (reg_def->reg) {
+ case SX9324_REG_AFE_PH0:
+ case SX9324_REG_AFE_PH1:
+ case SX9324_REG_AFE_PH2:
+ case SX9324_REG_AFE_PH3:
+ ph = reg_def->reg - SX9324_REG_AFE_PH0;
+ scnprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph);
+
+ count = device_property_count_u32(dev, prop);
+ if (count != ARRAY_SIZE(pin_defs))
+ break;
+ ret = device_property_read_u32_array(dev, prop, pin_defs,
+ ARRAY_SIZE(pin_defs));
+ for (pin = 0; pin < SX9324_NUM_PINS; pin++)
+ raw |= (pin_defs[pin] << (2 * pin)) &
+ SX9324_REG_AFE_PH0_PIN_MASK(pin);
+ reg_def->def = raw;
+ break;
+ case SX9324_REG_AFE_CTRL4:
+ case SX9324_REG_AFE_CTRL7:
+ if (reg_def->reg == SX9324_REG_AFE_CTRL4)
+ strncpy(prop, "semtech,ph01-resolution",
+ ARRAY_SIZE(prop));
+ else
+ strncpy(prop, "semtech,ph23-resolution",
+ ARRAY_SIZE(prop));
+
+ ret = device_property_read_u32(dev, prop, &raw);
+ if (ret)
+ break;
+
+ raw = ilog2(raw) - 3;
+
+ reg_def->def &= ~SX9324_REG_AFE_CTRL4_RESOLUTION_MASK;
+ reg_def->def |= FIELD_PREP(SX9324_REG_AFE_CTRL4_RESOLUTION_MASK,
+ raw);
+ break;
+ case SX9324_REG_ADV_CTRL5:
+ ret = device_property_read_u32(dev, "semtech,startup-sensor",
+ &start);
+ if (ret)
+ break;
+
+ reg_def->def &= ~SX9324_REG_ADV_CTRL5_STARTUPSENS_MASK;
+ reg_def->def |= FIELD_PREP(SX9324_REG_ADV_CTRL5_STARTUPSENS_MASK,
+ start);
+ break;
+ case SX9324_REG_PROX_CTRL4:
+ ret = device_property_read_u32(dev, "semtech,avg-pos-strength",
+ &pos);
+ if (ret)
+ break;
+
+ /* Powers of 2, except for a gap between 16 and 64 */
+ raw = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3);
+
+ reg_def->def &= ~SX9324_REG_PROX_CTRL4_AVGPOSFILT_MASK;
+ reg_def->def |= FIELD_PREP(SX9324_REG_PROX_CTRL4_AVGPOSFILT_MASK,
+ raw);
+ break;
+ case SX9324_REG_PROX_CTRL0:
+ case SX9324_REG_PROX_CTRL1:
+ if (reg_def->reg == SX9324_REG_PROX_CTRL0)
+ strncpy(prop, "semtech,ph01-proxraw-strength",
+ ARRAY_SIZE(prop));
+ else
+ strncpy(prop, "semtech,ph23-proxraw-strength",
+ ARRAY_SIZE(prop));
+ ret = device_property_read_u32(dev, prop, &raw);
+ if (ret)
+ break;
+
+ reg_def->def &= ~SX9324_REG_PROX_CTRL0_RAWFILT_MASK;
+ reg_def->def |= FIELD_PREP(SX9324_REG_PROX_CTRL0_RAWFILT_MASK,
+ raw);
+ break;
+ }
+ return reg_def;
+}
+
+static int sx9324_check_whoami(struct device *dev,
+ struct iio_dev *indio_dev)
+{
+ /*
+ * Only one sensor for this driver. Assuming the device tree
+ * is correct, just set the sensor name.
+ */
+ indio_dev->name = "sx9324";
+ return 0;
+}
+
+static const struct sx_common_chip_info sx9324_chip_info = {
+ .reg_stat = SX9324_REG_STAT0,
+ .reg_irq_msk = SX9324_REG_IRQ_MSK,
+ .reg_enable_chan = SX9324_REG_GNRL_CTRL1,
+ .reg_reset = SX9324_REG_RESET,
+
+ .mask_enable_chan = SX9324_REG_GNRL_CTRL1_PHEN_MASK,
+ .irq_msk_offset = 3,
+ .num_channels = SX9324_NUM_CHANNELS,
+ .num_default_regs = ARRAY_SIZE(sx9324_default_regs),
+
+ .ops = {
+ .read_prox_data = sx9324_read_prox_data,
+ .check_whoami = sx9324_check_whoami,
+ .init_compensation = sx9324_init_compensation,
+ .wait_for_sample = sx9324_wait_for_sample,
+ .get_default_reg = sx9324_get_default_reg,
+ },
+
+ .iio_channels = sx9324_channels,
+ .num_iio_channels = ARRAY_SIZE(sx9324_channels),
+ .iio_info = {
+ .read_raw = sx9324_read_raw,
+ .read_avail = sx9324_read_avail,
+ .read_event_value = sx9324_read_event_val,
+ .write_event_value = sx9324_write_event_val,
+ .write_raw = sx9324_write_raw,
+ .read_event_config = sx_common_read_event_config,
+ .write_event_config = sx_common_write_event_config,
+ },
+};
+
+static int sx9324_probe(struct i2c_client *client)
+{
+ return sx_common_probe(client, &sx9324_chip_info, &sx9324_regmap_config);
+}
+
+static int __maybe_unused sx9324_suspend(struct device *dev)
+{
+ struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
+ unsigned int regval;
+ int ret;
+
+ disable_irq_nosync(data->client->irq);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL1, &regval);
+
+ data->suspend_ctrl =
+ FIELD_GET(SX9324_REG_GNRL_CTRL1_PHEN_MASK, regval);
+
+ if (ret < 0)
+ goto out;
+
+ /* Disable all phases, send the device to sleep. */
+ ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0);
+
+out:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int __maybe_unused sx9324_resume(struct device *dev)
+{
+ struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1,
+ data->suspend_ctrl | SX9324_REG_GNRL_CTRL1_PAUSECTRL);
+ mutex_unlock(&data->mutex);
+ if (ret)
+ return ret;
+
+ enable_irq(data->client->irq);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume);
+
+static const struct acpi_device_id sx9324_acpi_match[] = {
+ { "STH9324", SX9324_WHOAMI_VALUE },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, sx9324_acpi_match);
+
+static const struct of_device_id sx9324_of_match[] = {
+ { .compatible = "semtech,sx9324", (void *)SX9324_WHOAMI_VALUE },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sx9324_of_match);
+
+static const struct i2c_device_id sx9324_id[] = {
+ { "sx9324", SX9324_WHOAMI_VALUE },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sx9324_id);
+
+static struct i2c_driver sx9324_driver = {
+ .driver = {
+ .name = "sx9324",
+ .acpi_match_table = sx9324_acpi_match,
+ .of_match_table = sx9324_of_match,
+ .pm = &sx9324_pm_ops,
+
+ /*
+ * Lots of i2c transfers in probe + over 200 ms waiting in
+ * sx9324_init_compensation() mean a slow probe; prefer async
+ * so we don't delay boot if we're builtin to the kernel.
+ */
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe_new = sx9324_probe,
+ .id_table = sx9324_id,
+};
+module_i2c_driver(sx9324_driver);
+
+MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
+MODULE_DESCRIPTION("Driver for Semtech SX9324 proximity sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(SEMTECH_PROX);
diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c
new file mode 100644
index 000000000000..3ebb30c8a4f6
--- /dev/null
+++ b/drivers/iio/proximity/sx9360.c
@@ -0,0 +1,893 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Driver for Semtech's SX9360 capacitive proximity/button solution.
+ * Based on SX9360 driver and copy of datasheet at:
+ * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+
+#include "sx_common.h"
+
+/* Nominal Oscillator Frequency. */
+#define SX9360_FOSC_MHZ 4
+#define SX9360_FOSC_HZ (SX9360_FOSC_MHZ * 1000000)
+
+/* Register definitions. */
+#define SX9360_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC
+#define SX9360_REG_STAT 0x01
+#define SX9360_REG_STAT_COMPSTAT_MASK GENMASK(2, 1)
+#define SX9360_REG_IRQ_MSK 0x02
+#define SX9360_CONVDONE_IRQ BIT(0)
+#define SX9360_FAR_IRQ BIT(2)
+#define SX9360_CLOSE_IRQ BIT(3)
+#define SX9360_REG_IRQ_CFG 0x03
+
+#define SX9360_REG_GNRL_CTRL0 0x10
+#define SX9360_REG_GNRL_CTRL0_PHEN_MASK GENMASK(1, 0)
+#define SX9360_REG_GNRL_CTRL1 0x11
+#define SX9360_REG_GNRL_CTRL1_SCANPERIOD_MASK GENMASK(2, 0)
+#define SX9360_REG_GNRL_CTRL2 0x12
+#define SX9360_REG_GNRL_CTRL2_PERIOD_102MS 0x32
+#define SX9360_REG_GNRL_REG_2_PERIOD_MS(_r) \
+ (((_r) * 8192) / (SX9360_FOSC_HZ / 1000))
+#define SX9360_REG_GNRL_FREQ_2_REG(_f) (((_f) * 8192) / SX9360_FOSC_HZ)
+#define SX9360_REG_GNRL_REG_2_FREQ(_r) (SX9360_FOSC_HZ / ((_r) * 8192))
+
+#define SX9360_REG_AFE_CTRL1 0x21
+#define SX9360_REG_AFE_PARAM0_PHR 0x22
+#define SX9360_REG_AFE_PARAM1_PHR 0x23
+#define SX9360_REG_AFE_PARAM0_PHM 0x24
+#define SX9360_REG_AFE_PARAM0_RSVD 0x08
+#define SX9360_REG_AFE_PARAM0_RESOLUTION_MASK GENMASK(2, 0)
+#define SX9360_REG_AFE_PARAM0_RESOLUTION_128 0x02
+#define SX9360_REG_AFE_PARAM1_PHM 0x25
+#define SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF 0x40
+#define SX9360_REG_AFE_PARAM1_FREQ_83_33HZ 0x06
+
+#define SX9360_REG_PROX_CTRL0_PHR 0x40
+#define SX9360_REG_PROX_CTRL0_PHM 0x41
+#define SX9360_REG_PROX_CTRL0_GAIN_MASK GENMASK(5, 3)
+#define SX9360_REG_PROX_CTRL0_GAIN_1 0x80
+#define SX9360_REG_PROX_CTRL0_RAWFILT_MASK GENMASK(2, 0)
+#define SX9360_REG_PROX_CTRL0_RAWFILT_1P50 0x01
+#define SX9360_REG_PROX_CTRL1 0x42
+#define SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_MASK GENMASK(5, 3)
+#define SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K 0x20
+#define SX9360_REG_PROX_CTRL2 0x43
+#define SX9360_REG_PROX_CTRL2_AVGDEB_MASK GENMASK(7, 6)
+#define SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES 0x40
+#define SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K 0x20
+#define SX9360_REG_PROX_CTRL3 0x44
+#define SX9360_REG_PROX_CTRL3_AVGNEG_FILT_MASK GENMASK(5, 3)
+#define SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 0x08
+#define SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK GENMASK(2, 0)
+#define SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256 0x04
+#define SX9360_REG_PROX_CTRL4 0x45
+#define SX9360_REG_PROX_CTRL4_HYST_MASK GENMASK(5, 4)
+#define SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK GENMASK(3, 2)
+#define SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK GENMASK(1, 0)
+#define SX9360_REG_PROX_CTRL5 0x46
+#define SX9360_REG_PROX_CTRL5_PROXTHRESH_32 0x08
+
+#define SX9360_REG_REF_CORR0 0x60
+#define SX9360_REG_REF_CORR1 0x61
+
+#define SX9360_REG_USEFUL_PHR_MSB 0x90
+#define SX9360_REG_USEFUL_PHR_LSB 0x91
+
+#define SX9360_REG_OFFSET_PMR_MSB 0x92
+#define SX9360_REG_OFFSET_PMR_LSB 0x93
+
+#define SX9360_REG_USEFUL_PHM_MSB 0x94
+#define SX9360_REG_USEFUL_PHM_LSB 0x95
+
+#define SX9360_REG_AVG_PHM_MSB 0x96
+#define SX9360_REG_AVG_PHM_LSB 0x97
+
+#define SX9360_REG_DIFF_PHM_MSB 0x98
+#define SX9360_REG_DIFF_PHM_LSB 0x99
+
+#define SX9360_REG_OFFSET_PHM_MSB 0x9a
+#define SX9360_REG_OFFSET_PHM_LSB 0x9b
+
+#define SX9360_REG_USE_FILTER_MSB 0x9a
+#define SX9360_REG_USE_FILTER_LSB 0x9b
+
+#define SX9360_REG_RESET 0xcf
+/* Write this to REG_RESET to do a soft reset. */
+#define SX9360_SOFT_RESET 0xde
+
+#define SX9360_REG_WHOAMI 0xfa
+#define SX9360_WHOAMI_VALUE 0x60
+
+#define SX9360_REG_REVISION 0xfe
+
+/* 2 channels, Phase Reference and Measurement. */
+#define SX9360_NUM_CHANNELS 2
+
+static const struct iio_chan_spec sx9360_channels[] = {
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .indexed = 1,
+ .address = SX9360_REG_USEFUL_PHR_MSB,
+ .channel = 0,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .indexed = 1,
+ .address = SX9360_REG_USEFUL_PHM_MSB,
+ .event_spec = sx_common_events,
+ .num_event_specs = ARRAY_SIZE(sx_common_events),
+ .channel = 1,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+/*
+ * Each entry contains the integer part (val) and the fractional part, in micro
+ * seconds. It conforms to the IIO output IIO_VAL_INT_PLUS_MICRO.
+ *
+ * The frequency control register holds the period, with a ~2ms increment.
+ * Therefore the smallest frequency is 4MHz / (2047 * 8192),
+ * The fastest is 4MHz / 8192.
+ * The interval is not linear, but given there is 2047 possible value,
+ * Returns the fake increment of (Max-Min)/2047
+ */
+static const struct {
+ int val;
+ int val2;
+} sx9360_samp_freq_interval[] = {
+ { 0, 281250 }, /* 4MHz / (8192 * 2047) */
+ { 0, 281250 },
+ { 448, 281250 }, /* 4MHz / 8192 */
+};
+
+static const struct regmap_range sx9360_writable_reg_ranges[] = {
+ /*
+ * To set COMPSTAT for compensation, even if datasheet says register is
+ * RO.
+ */
+ regmap_reg_range(SX9360_REG_STAT, SX9360_REG_IRQ_CFG),
+ regmap_reg_range(SX9360_REG_GNRL_CTRL0, SX9360_REG_GNRL_CTRL2),
+ regmap_reg_range(SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_PARAM1_PHM),
+ regmap_reg_range(SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL5),
+ regmap_reg_range(SX9360_REG_REF_CORR0, SX9360_REG_REF_CORR1),
+ regmap_reg_range(SX9360_REG_OFFSET_PMR_MSB, SX9360_REG_OFFSET_PMR_LSB),
+ regmap_reg_range(SX9360_REG_RESET, SX9360_REG_RESET),
+};
+
+static const struct regmap_access_table sx9360_writeable_regs = {
+ .yes_ranges = sx9360_writable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sx9360_writable_reg_ranges),
+};
+
+/*
+ * All allocated registers are readable, so we just list unallocated
+ * ones.
+ */
+static const struct regmap_range sx9360_non_readable_reg_ranges[] = {
+ regmap_reg_range(SX9360_REG_IRQ_CFG + 1, SX9360_REG_GNRL_CTRL0 - 1),
+ regmap_reg_range(SX9360_REG_GNRL_CTRL2 + 1, SX9360_REG_AFE_CTRL1 - 1),
+ regmap_reg_range(SX9360_REG_AFE_PARAM1_PHM + 1,
+ SX9360_REG_PROX_CTRL0_PHR - 1),
+ regmap_reg_range(SX9360_REG_PROX_CTRL5 + 1, SX9360_REG_REF_CORR0 - 1),
+ regmap_reg_range(SX9360_REG_REF_CORR1 + 1,
+ SX9360_REG_USEFUL_PHR_MSB - 1),
+ regmap_reg_range(SX9360_REG_USE_FILTER_LSB + 1, SX9360_REG_RESET - 1),
+ regmap_reg_range(SX9360_REG_RESET + 1, SX9360_REG_WHOAMI - 1),
+ regmap_reg_range(SX9360_REG_WHOAMI + 1, SX9360_REG_REVISION - 1),
+};
+
+static const struct regmap_access_table sx9360_readable_regs = {
+ .no_ranges = sx9360_non_readable_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(sx9360_non_readable_reg_ranges),
+};
+
+static const struct regmap_range sx9360_volatile_reg_ranges[] = {
+ regmap_reg_range(SX9360_REG_IRQ_SRC, SX9360_REG_STAT),
+ regmap_reg_range(SX9360_REG_USEFUL_PHR_MSB, SX9360_REG_USE_FILTER_LSB),
+ regmap_reg_range(SX9360_REG_WHOAMI, SX9360_REG_WHOAMI),
+ regmap_reg_range(SX9360_REG_REVISION, SX9360_REG_REVISION),
+};
+
+static const struct regmap_access_table sx9360_volatile_regs = {
+ .yes_ranges = sx9360_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sx9360_volatile_reg_ranges),
+};
+
+static const struct regmap_config sx9360_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SX9360_REG_REVISION,
+ .cache_type = REGCACHE_RBTREE,
+
+ .wr_table = &sx9360_writeable_regs,
+ .rd_table = &sx9360_readable_regs,
+ .volatile_table = &sx9360_volatile_regs,
+};
+
+static int sx9360_read_prox_data(struct sx_common_data *data,
+ const struct iio_chan_spec *chan,
+ __be16 *val)
+{
+ return regmap_bulk_read(data->regmap, chan->address, val, sizeof(*val));
+}
+
+/*
+ * If we have no interrupt support, we have to wait for a scan period
+ * after enabling a channel to get a result.
+ */
+static int sx9360_wait_for_sample(struct sx_common_data *data)
+{
+ int ret;
+ __be16 buf;
+
+ ret = regmap_bulk_read(data->regmap, SX9360_REG_GNRL_CTRL1,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ msleep(SX9360_REG_GNRL_REG_2_PERIOD_MS(be16_to_cpu(buf)));
+
+ return 0;
+}
+
+static int sx9360_read_gain(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int *val)
+{
+ unsigned int reg, regval;
+ int ret;
+
+ reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel;
+ ret = regmap_read(data->regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ *val = 1 << FIELD_GET(SX9360_REG_PROX_CTRL0_GAIN_MASK, regval);
+
+ return IIO_VAL_INT;
+}
+
+static int sx9360_read_samp_freq(struct sx_common_data *data,
+ int *val, int *val2)
+{
+ int ret, divisor;
+ __be16 buf;
+
+ ret = regmap_bulk_read(data->regmap, SX9360_REG_GNRL_CTRL1,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ divisor = be16_to_cpu(buf);
+ if (divisor == 0) {
+ *val = 0;
+ return IIO_VAL_INT;
+ }
+
+ *val = SX9360_FOSC_HZ;
+ *val2 = divisor * 8192;
+
+ return IIO_VAL_FRACTIONAL;
+}
+
+static int sx9360_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = sx_common_read_proximity(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = sx9360_read_gain(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return sx9360_read_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const char *sx9360_channel_labels[SX9360_NUM_CHANNELS] = {
+ "reference", "main",
+};
+
+static int sx9360_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan,
+ char *label)
+{
+ return sysfs_emit(label, "%s\n", sx9360_channel_labels[chan->channel]);
+}
+
+static const int sx9360_gain_vals[] = { 1, 2, 4, 8 };
+
+static int sx9360_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ if (chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(sx9360_gain_vals);
+ *vals = sx9360_gain_vals;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(sx9360_samp_freq_interval) * 2;
+ *vals = (int *)sx9360_samp_freq_interval;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9360_set_samp_freq(struct sx_common_data *data,
+ int val, int val2)
+{
+ int ret, reg;
+ __be16 buf;
+
+ reg = val * 8192 / SX9360_FOSC_HZ + val2 * 8192 / (SX9360_FOSC_MHZ);
+ buf = cpu_to_be16(reg);
+ mutex_lock(&data->mutex);
+
+ ret = regmap_bulk_write(data->regmap, SX9360_REG_GNRL_CTRL1, &buf,
+ sizeof(buf));
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9360_read_thresh(struct sx_common_data *data, int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL5, &regval);
+ if (ret)
+ return ret;
+
+ if (regval <= 1)
+ *val = regval;
+ else
+ *val = (regval * regval) / 2;
+
+ return IIO_VAL_INT;
+}
+
+static int sx9360_read_hysteresis(struct sx_common_data *data, int *val)
+{
+ unsigned int regval, pthresh;
+ int ret;
+
+ ret = sx9360_read_thresh(data, &pthresh);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL4, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9360_REG_PROX_CTRL4_HYST_MASK, regval);
+ if (!regval)
+ *val = 0;
+ else
+ *val = pthresh >> (5 - regval);
+
+ return IIO_VAL_INT;
+}
+
+static int sx9360_read_far_debounce(struct sx_common_data *data, int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL4, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, regval);
+ if (regval)
+ *val = 1 << regval;
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+}
+
+static int sx9360_read_close_debounce(struct sx_common_data *data, int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, SX9360_REG_PROX_CTRL4, &regval);
+ if (ret)
+ return ret;
+
+ regval = FIELD_GET(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, regval);
+ if (regval)
+ *val = 1 << regval;
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+}
+
+static int sx9360_read_event_val(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 sx_common_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return sx9360_read_thresh(data, val);
+ case IIO_EV_INFO_PERIOD:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return sx9360_read_far_debounce(data, val);
+ case IIO_EV_DIR_FALLING:
+ return sx9360_read_close_debounce(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_HYSTERESIS:
+ return sx9360_read_hysteresis(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9360_write_thresh(struct sx_common_data *data, int _val)
+{
+ unsigned int val = _val;
+ int ret;
+
+ if (val >= 1)
+ val = int_sqrt(2 * val);
+
+ if (val > 0xff)
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_write(data->regmap, SX9360_REG_PROX_CTRL5, val);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9360_write_hysteresis(struct sx_common_data *data, int _val)
+{
+ unsigned int hyst, val = _val;
+ int ret, pthresh;
+
+ ret = sx9360_read_thresh(data, &pthresh);
+ if (ret < 0)
+ return ret;
+
+ if (val == 0)
+ hyst = 0;
+ else if (val >= pthresh >> 2)
+ hyst = 3;
+ else if (val >= pthresh >> 3)
+ hyst = 2;
+ else if (val >= pthresh >> 4)
+ hyst = 1;
+ else
+ return -EINVAL;
+
+ hyst = FIELD_PREP(SX9360_REG_PROX_CTRL4_HYST_MASK, hyst);
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4,
+ SX9360_REG_PROX_CTRL4_HYST_MASK, hyst);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9360_write_far_debounce(struct sx_common_data *data, int _val)
+{
+ unsigned int regval, val = _val;
+ int ret;
+
+ if (val > 0)
+ val = ilog2(val);
+ if (!FIELD_FIT(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val))
+ return -EINVAL;
+
+ regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4,
+ SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK,
+ regval);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9360_write_close_debounce(struct sx_common_data *data, int _val)
+{
+ unsigned int regval, val = _val;
+ int ret;
+
+ if (val > 0)
+ val = ilog2(val);
+ if (!FIELD_FIT(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val))
+ return -EINVAL;
+
+ regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4,
+ SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK,
+ regval);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9360_write_event_val(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 sx_common_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return sx9360_write_thresh(data, val);
+ case IIO_EV_INFO_PERIOD:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return sx9360_write_far_debounce(data, val);
+ case IIO_EV_DIR_FALLING:
+ return sx9360_write_close_debounce(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_HYSTERESIS:
+ return sx9360_write_hysteresis(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9360_write_gain(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int val)
+{
+ unsigned int gain, reg;
+ int ret;
+
+ gain = ilog2(val);
+ reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel;
+ gain = FIELD_PREP(SX9360_REG_PROX_CTRL0_GAIN_MASK, gain);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, reg,
+ SX9360_REG_PROX_CTRL0_GAIN_MASK,
+ gain);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9360_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2,
+ long mask)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return sx9360_set_samp_freq(data, val, val2);
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ return sx9360_write_gain(data, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct sx_common_reg_default sx9360_default_regs[] = {
+ { SX9360_REG_IRQ_MSK, 0x00 },
+ { SX9360_REG_IRQ_CFG, 0x00 },
+ /*
+ * The lower 2 bits should not be set as it enable sensors measurements.
+ * Turning the detection on before the configuration values are set to
+ * good values can cause the device to return erroneous readings.
+ */
+ { SX9360_REG_GNRL_CTRL0, 0x00 },
+ { SX9360_REG_GNRL_CTRL1, 0x00 },
+ { SX9360_REG_GNRL_CTRL2, SX9360_REG_GNRL_CTRL2_PERIOD_102MS },
+
+ { SX9360_REG_AFE_CTRL1, 0x00 },
+ { SX9360_REG_AFE_PARAM0_PHR, SX9360_REG_AFE_PARAM0_RSVD |
+ SX9360_REG_AFE_PARAM0_RESOLUTION_128 },
+ { SX9360_REG_AFE_PARAM1_PHR, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF |
+ SX9360_REG_AFE_PARAM1_FREQ_83_33HZ },
+ { SX9360_REG_AFE_PARAM0_PHM, SX9360_REG_AFE_PARAM0_RSVD |
+ SX9360_REG_AFE_PARAM0_RESOLUTION_128 },
+ { SX9360_REG_AFE_PARAM1_PHM, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF |
+ SX9360_REG_AFE_PARAM1_FREQ_83_33HZ },
+
+ { SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL0_GAIN_1 |
+ SX9360_REG_PROX_CTRL0_RAWFILT_1P50 },
+ { SX9360_REG_PROX_CTRL0_PHM, SX9360_REG_PROX_CTRL0_GAIN_1 |
+ SX9360_REG_PROX_CTRL0_RAWFILT_1P50 },
+ { SX9360_REG_PROX_CTRL1, SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K },
+ { SX9360_REG_PROX_CTRL2, SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES |
+ SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K },
+ { SX9360_REG_PROX_CTRL3, SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 |
+ SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256 },
+ { SX9360_REG_PROX_CTRL4, 0x00 },
+ { SX9360_REG_PROX_CTRL5, SX9360_REG_PROX_CTRL5_PROXTHRESH_32 },
+};
+
+/* Activate all channels and perform an initial compensation. */
+static int sx9360_init_compensation(struct iio_dev *indio_dev)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ /* run the compensation phase on all channels */
+ ret = regmap_update_bits(data->regmap, SX9360_REG_STAT,
+ SX9360_REG_STAT_COMPSTAT_MASK,
+ SX9360_REG_STAT_COMPSTAT_MASK);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(data->regmap, SX9360_REG_STAT, val,
+ !(val & SX9360_REG_STAT_COMPSTAT_MASK),
+ 20000, 2000000);
+}
+
+static const struct sx_common_reg_default *
+sx9360_get_default_reg(struct device *dev, int idx,
+ struct sx_common_reg_default *reg_def)
+{
+ u32 raw = 0, pos = 0;
+ int ret;
+
+ memcpy(reg_def, &sx9360_default_regs[idx], sizeof(*reg_def));
+ switch (reg_def->reg) {
+ case SX9360_REG_AFE_PARAM0_PHR:
+ case SX9360_REG_AFE_PARAM0_PHM:
+ ret = device_property_read_u32(dev, "semtech,resolution", &raw);
+ if (ret)
+ break;
+
+ raw = ilog2(raw) - 3;
+
+ reg_def->def &= ~SX9360_REG_AFE_PARAM0_RESOLUTION_MASK;
+ reg_def->def |= FIELD_PREP(SX9360_REG_AFE_PARAM0_RESOLUTION_MASK, raw);
+ break;
+ case SX9360_REG_PROX_CTRL0_PHR:
+ case SX9360_REG_PROX_CTRL0_PHM:
+ ret = device_property_read_u32(dev, "semtech,proxraw-strength", &raw);
+ if (ret)
+ break;
+
+ reg_def->def &= ~SX9360_REG_PROX_CTRL0_RAWFILT_MASK;
+ reg_def->def |= FIELD_PREP(SX9360_REG_PROX_CTRL0_RAWFILT_MASK, raw);
+ break;
+ case SX9360_REG_PROX_CTRL3:
+ ret = device_property_read_u32(dev, "semtech,avg-pos-strength",
+ &pos);
+ if (ret)
+ break;
+
+ /* Powers of 2, except for a gap between 16 and 64 */
+ raw = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3);
+ reg_def->def &= ~SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK;
+ reg_def->def |= FIELD_PREP(SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK, raw);
+ break;
+ }
+
+ return reg_def;
+}
+
+static int sx9360_check_whoami(struct device *dev, struct iio_dev *indio_dev)
+{
+ /*
+ * Only one sensor for this driver. Assuming the device tree
+ * is correct, just set the sensor name.
+ */
+ indio_dev->name = "sx9360";
+ return 0;
+}
+
+static const struct sx_common_chip_info sx9360_chip_info = {
+ .reg_stat = SX9360_REG_STAT,
+ .reg_irq_msk = SX9360_REG_IRQ_MSK,
+ .reg_enable_chan = SX9360_REG_GNRL_CTRL0,
+ .reg_reset = SX9360_REG_RESET,
+
+ .mask_enable_chan = SX9360_REG_GNRL_CTRL0_PHEN_MASK,
+ .stat_offset = 2,
+ .num_channels = SX9360_NUM_CHANNELS,
+ .num_default_regs = ARRAY_SIZE(sx9360_default_regs),
+
+ .ops = {
+ .read_prox_data = sx9360_read_prox_data,
+ .check_whoami = sx9360_check_whoami,
+ .init_compensation = sx9360_init_compensation,
+ .wait_for_sample = sx9360_wait_for_sample,
+ .get_default_reg = sx9360_get_default_reg,
+ },
+
+ .iio_channels = sx9360_channels,
+ .num_iio_channels = ARRAY_SIZE(sx9360_channels),
+ .iio_info = {
+ .read_raw = sx9360_read_raw,
+ .read_avail = sx9360_read_avail,
+ .read_label = sx9360_read_label,
+ .read_event_value = sx9360_read_event_val,
+ .write_event_value = sx9360_write_event_val,
+ .write_raw = sx9360_write_raw,
+ .read_event_config = sx_common_read_event_config,
+ .write_event_config = sx_common_write_event_config,
+ },
+};
+
+static int sx9360_probe(struct i2c_client *client)
+{
+ return sx_common_probe(client, &sx9360_chip_info, &sx9360_regmap_config);
+}
+
+static int __maybe_unused sx9360_suspend(struct device *dev)
+{
+ struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
+ unsigned int regval;
+ int ret;
+
+ disable_irq_nosync(data->client->irq);
+
+ mutex_lock(&data->mutex);
+ ret = regmap_read(data->regmap, SX9360_REG_GNRL_CTRL0, &regval);
+
+ data->suspend_ctrl =
+ FIELD_GET(SX9360_REG_GNRL_CTRL0_PHEN_MASK, regval);
+
+ if (ret < 0)
+ goto out;
+
+ /* Disable all phases, send the device to sleep. */
+ ret = regmap_write(data->regmap, SX9360_REG_GNRL_CTRL0, 0);
+
+out:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int __maybe_unused sx9360_resume(struct device *dev)
+{
+ struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, SX9360_REG_GNRL_CTRL0,
+ SX9360_REG_GNRL_CTRL0_PHEN_MASK,
+ data->suspend_ctrl);
+ mutex_unlock(&data->mutex);
+ if (ret)
+ return ret;
+
+ enable_irq(data->client->irq);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume);
+
+static const struct acpi_device_id sx9360_acpi_match[] = {
+ { "STH9360", SX9360_WHOAMI_VALUE },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, sx9360_acpi_match);
+
+static const struct of_device_id sx9360_of_match[] = {
+ { .compatible = "semtech,sx9360", (void *)SX9360_WHOAMI_VALUE },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sx9360_of_match);
+
+static const struct i2c_device_id sx9360_id[] = {
+ {"sx9360", SX9360_WHOAMI_VALUE },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sx9360_id);
+
+static struct i2c_driver sx9360_driver = {
+ .driver = {
+ .name = "sx9360",
+ .acpi_match_table = sx9360_acpi_match,
+ .of_match_table = sx9360_of_match,
+ .pm = &sx9360_pm_ops,
+
+ /*
+ * Lots of i2c transfers in probe + over 200 ms waiting in
+ * sx9360_init_compensation() mean a slow probe; prefer async
+ * so we don't delay boot if we're builtin to the kernel.
+ */
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe_new = sx9360_probe,
+ .id_table = sx9360_id,
+};
+module_i2c_driver(sx9360_driver);
+
+MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
+MODULE_DESCRIPTION("Driver for Semtech SX9360 proximity sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(SEMTECH_PROX);
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
index 3e4ddb2e8c2b..42589d6200ad 100644
--- a/drivers/iio/proximity/sx9500.c
+++ b/drivers/iio/proximity/sx9500.c
@@ -993,7 +993,6 @@ static int sx9500_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int sx9500_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -1030,11 +1029,8 @@ static int sx9500_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-static const struct dev_pm_ops sx9500_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(sx9500_pm_ops, sx9500_suspend, sx9500_resume);
static const struct acpi_device_id sx9500_acpi_match[] = {
{"SSX9500", 0},
@@ -1060,7 +1056,7 @@ static struct i2c_driver sx9500_driver = {
.name = SX9500_DRIVER_NAME,
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
.of_match_table = of_match_ptr(sx9500_of_match),
- .pm = &sx9500_pm_ops,
+ .pm = pm_sleep_ptr(&sx9500_pm_ops),
},
.probe = sx9500_probe,
.remove = sx9500_remove,
diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c
new file mode 100644
index 000000000000..a7c07316a0a9
--- /dev/null
+++ b/drivers/iio/proximity/sx_common.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Common part of most Semtech SAR sensor.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <vdso/bits.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "sx_common.h"
+
+/* All Semtech SAR sensors have IRQ bit in the same order. */
+#define SX_COMMON_CONVDONE_IRQ BIT(0)
+#define SX_COMMON_FAR_IRQ BIT(2)
+#define SX_COMMON_CLOSE_IRQ BIT(3)
+
+const struct iio_event_spec sx_common_events[3] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_HYSTERESIS) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+EXPORT_SYMBOL_NS_GPL(sx_common_events, SEMTECH_PROX);
+
+static irqreturn_t sx_common_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sx_common_data *data = iio_priv(indio_dev);
+
+ if (data->trigger_enabled)
+ iio_trigger_poll(data->trig);
+
+ /*
+ * Even if no event is enabled, we need to wake the thread to clear the
+ * interrupt state by reading SX_COMMON_REG_IRQ_SRC.
+ * It is not possible to do that here because regmap_read takes a mutex.
+ */
+ return IRQ_WAKE_THREAD;
+}
+
+static void sx_common_push_events(struct iio_dev *indio_dev)
+{
+ int ret;
+ unsigned int val, chan;
+ struct sx_common_data *data = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns(indio_dev);
+ unsigned long prox_changed;
+
+ /* Read proximity state on all channels */
+ ret = regmap_read(data->regmap, data->chip_info->reg_stat, &val);
+ if (ret) {
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+ return;
+ }
+
+ val >>= data->chip_info->stat_offset;
+
+ /*
+ * Only iterate over channels with changes on proximity status that have
+ * events enabled.
+ */
+ prox_changed = (data->chan_prox_stat ^ val) & data->chan_event;
+
+ for_each_set_bit(chan, &prox_changed, data->chip_info->num_channels) {
+ int dir;
+ u64 ev;
+
+ dir = (val & BIT(chan)) ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
+ ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
+ IIO_EV_TYPE_THRESH, dir);
+
+ iio_push_event(indio_dev, ev, timestamp);
+ }
+ data->chan_prox_stat = val;
+}
+
+static int sx_common_enable_irq(struct sx_common_data *data, unsigned int irq)
+{
+ if (!data->client->irq)
+ return 0;
+ return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk,
+ irq << data->chip_info->irq_msk_offset,
+ irq << data->chip_info->irq_msk_offset);
+}
+
+static int sx_common_disable_irq(struct sx_common_data *data, unsigned int irq)
+{
+ if (!data->client->irq)
+ return 0;
+ return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk,
+ irq << data->chip_info->irq_msk_offset, 0);
+}
+
+static int sx_common_update_chan_en(struct sx_common_data *data,
+ unsigned long chan_read,
+ unsigned long chan_event)
+{
+ int ret;
+ unsigned long channels = chan_read | chan_event;
+
+ if ((data->chan_read | data->chan_event) != channels) {
+ ret = regmap_update_bits(data->regmap,
+ data->chip_info->reg_enable_chan,
+ data->chip_info->mask_enable_chan,
+ channels);
+ if (ret)
+ return ret;
+ }
+ data->chan_read = chan_read;
+ data->chan_event = chan_event;
+ return 0;
+}
+
+static int sx_common_get_read_channel(struct sx_common_data *data, int channel)
+{
+ return sx_common_update_chan_en(data, data->chan_read | BIT(channel),
+ data->chan_event);
+}
+
+static int sx_common_put_read_channel(struct sx_common_data *data, int channel)
+{
+ return sx_common_update_chan_en(data, data->chan_read & ~BIT(channel),
+ data->chan_event);
+}
+
+static int sx_common_get_event_channel(struct sx_common_data *data, int channel)
+{
+ return sx_common_update_chan_en(data, data->chan_read,
+ data->chan_event | BIT(channel));
+}
+
+static int sx_common_put_event_channel(struct sx_common_data *data, int channel)
+{
+ return sx_common_update_chan_en(data, data->chan_read,
+ data->chan_event & ~BIT(channel));
+}
+
+/**
+ * sx_common_read_proximity() - Read raw proximity value.
+ * @data: Internal data
+ * @chan: Channel to read
+ * @val: pointer to return read value.
+ *
+ * Request a conversion, wait for the sensor to be ready and
+ * return the raw proximity value.
+ */
+int sx_common_read_proximity(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int *val)
+{
+ int ret;
+ __be16 rawval;
+
+ mutex_lock(&data->mutex);
+
+ ret = sx_common_get_read_channel(data, chan->channel);
+ if (ret)
+ goto out;
+
+ ret = sx_common_enable_irq(data, SX_COMMON_CONVDONE_IRQ);
+ if (ret)
+ goto out_put_channel;
+
+ mutex_unlock(&data->mutex);
+
+ if (data->client->irq) {
+ ret = wait_for_completion_interruptible(&data->completion);
+ reinit_completion(&data->completion);
+ } else {
+ ret = data->chip_info->ops.wait_for_sample(data);
+ }
+
+ mutex_lock(&data->mutex);
+
+ if (ret)
+ goto out_disable_irq;
+
+ ret = data->chip_info->ops.read_prox_data(data, chan, &rawval);
+ if (ret)
+ goto out_disable_irq;
+
+ *val = sign_extend32(be16_to_cpu(rawval), chan->scan_type.realbits - 1);
+
+ ret = sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ);
+ if (ret)
+ goto out_put_channel;
+
+ ret = sx_common_put_read_channel(data, chan->channel);
+ if (ret)
+ goto out;
+
+ mutex_unlock(&data->mutex);
+
+ return IIO_VAL_INT;
+
+out_disable_irq:
+ sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ);
+out_put_channel:
+ sx_common_put_read_channel(data, chan->channel);
+out:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sx_common_read_proximity, SEMTECH_PROX);
+
+/**
+ * sx_common_read_event_config() - Configure event setting.
+ * @indio_dev: iio device object
+ * @chan: Channel to read
+ * @type: Type of event (unused)
+ * @dir: Direction of event (unused)
+ *
+ * return if the given channel is used for event gathering.
+ */
+int sx_common_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 sx_common_data *data = iio_priv(indio_dev);
+
+ return !!(data->chan_event & BIT(chan->channel));
+}
+EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, SEMTECH_PROX);
+
+/**
+ * sx_common_write_event_config() - Configure event setting.
+ * @indio_dev: iio device object
+ * @chan: Channel to enable
+ * @type: Type of event (unused)
+ * @dir: Direction of event (unused)
+ * @state: State of the event.
+ *
+ * Enable/Disable event on a given channel.
+ */
+int sx_common_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ unsigned int eventirq = SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ;
+ int ret;
+
+ /* If the state hasn't changed, there's nothing to do. */
+ if (!!(data->chan_event & BIT(chan->channel)) == state)
+ return 0;
+
+ mutex_lock(&data->mutex);
+ if (state) {
+ ret = sx_common_get_event_channel(data, chan->channel);
+ if (ret)
+ goto out_unlock;
+ if (!(data->chan_event & ~BIT(chan->channel))) {
+ ret = sx_common_enable_irq(data, eventirq);
+ if (ret)
+ sx_common_put_event_channel(data, chan->channel);
+ }
+ } else {
+ ret = sx_common_put_event_channel(data, chan->channel);
+ if (ret)
+ goto out_unlock;
+ if (!data->chan_event) {
+ ret = sx_common_disable_irq(data, eventirq);
+ if (ret)
+ sx_common_get_event_channel(data, chan->channel);
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sx_common_write_event_config, SEMTECH_PROX);
+
+static int sx_common_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct sx_common_data *data = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&data->mutex);
+
+ if (state)
+ ret = sx_common_enable_irq(data, SX_COMMON_CONVDONE_IRQ);
+ else if (!data->chan_read)
+ ret = sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ);
+ if (ret)
+ goto out;
+
+ data->trigger_enabled = state;
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct iio_trigger_ops sx_common_trigger_ops = {
+ .set_trigger_state = sx_common_set_trigger_state,
+};
+
+static irqreturn_t sx_common_irq_thread_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sx_common_data *data = iio_priv(indio_dev);
+ int ret;
+ unsigned int val;
+
+ mutex_lock(&data->mutex);
+
+ ret = regmap_read(data->regmap, SX_COMMON_REG_IRQ_SRC, &val);
+ if (ret) {
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+ goto out;
+ }
+
+ if (val & ((SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ) << data->chip_info->irq_msk_offset))
+ sx_common_push_events(indio_dev);
+
+ if (val & (SX_COMMON_CONVDONE_IRQ << data->chip_info->irq_msk_offset))
+ complete(&data->completion);
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sx_common_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct sx_common_data *data = iio_priv(indio_dev);
+ __be16 val;
+ int bit, ret, i = 0;
+
+ mutex_lock(&data->mutex);
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = data->chip_info->ops.read_prox_data(data,
+ &indio_dev->channels[bit],
+ &val);
+ if (ret)
+ goto out;
+
+ data->buffer.channels[i++] = val;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
+ pf->timestamp);
+
+out:
+ mutex_unlock(&data->mutex);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int sx_common_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ unsigned long channels = 0;
+ int bit, ret;
+
+ mutex_lock(&data->mutex);
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength)
+ __set_bit(indio_dev->channels[bit].channel, &channels);
+
+ ret = sx_common_update_chan_en(data, channels, data->chan_event);
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int sx_common_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = sx_common_update_chan_en(data, 0, data->chan_event);
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = {
+ .preenable = sx_common_buffer_preenable,
+ .postdisable = sx_common_buffer_postdisable,
+};
+
+static void sx_common_regulator_disable(void *_data)
+{
+ struct sx_common_data *data = _data;
+
+ regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies);
+}
+
+#define SX_COMMON_SOFT_RESET 0xde
+
+static int sx_common_init_device(struct iio_dev *indio_dev)
+{
+ struct sx_common_data *data = iio_priv(indio_dev);
+ struct sx_common_reg_default tmp;
+ const struct sx_common_reg_default *initval;
+ int ret;
+ unsigned int i, val;
+
+ ret = regmap_write(data->regmap, data->chip_info->reg_reset,
+ SX_COMMON_SOFT_RESET);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000); /* power-up time is ~1ms. */
+
+ /* Clear reset interrupt state by reading SX_COMMON_REG_IRQ_SRC. */
+ ret = regmap_read(data->regmap, SX_COMMON_REG_IRQ_SRC, &val);
+ if (ret)
+ return ret;
+
+ /* Program defaults from constant or BIOS. */
+ for (i = 0; i < data->chip_info->num_default_regs; i++) {
+ initval = data->chip_info->ops.get_default_reg(&indio_dev->dev,
+ i, &tmp);
+ ret = regmap_write(data->regmap, initval->reg, initval->def);
+ if (ret)
+ return ret;
+ }
+
+ return data->chip_info->ops.init_compensation(indio_dev);
+}
+
+/**
+ * sx_common_probe() - Common setup for Semtech SAR sensor
+ * @client: I2C client object
+ * @chip_info: Semtech sensor chip information.
+ * @regmap_config: Sensor registers map configuration.
+ */
+int sx_common_probe(struct i2c_client *client,
+ const struct sx_common_chip_info *chip_info,
+ const struct regmap_config *regmap_config)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct sx_common_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ data->chip_info = chip_info;
+ data->client = client;
+ data->supplies[0].supply = "vdd";
+ data->supplies[1].supply = "svdd";
+ mutex_init(&data->mutex);
+ init_completion(&data->completion);
+
+ data->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "Could init register map\n");
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+ data->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to get regulators\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to enable regulators\n");
+
+ /* Must wait for Tpor time after initial power up */
+ usleep_range(1000, 1100);
+
+ ret = devm_add_action_or_reset(dev, sx_common_regulator_disable, data);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Unable to register regulators deleter\n");
+
+ ret = data->chip_info->ops.check_whoami(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "error reading WHOAMI\n");
+
+ ACPI_COMPANION_SET(&indio_dev->dev, ACPI_COMPANION(dev));
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = data->chip_info->iio_channels;
+ indio_dev->num_channels = data->chip_info->num_iio_channels;
+ indio_dev->info = &data->chip_info->iio_info;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ ret = sx_common_init_device(indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to initialize sensor\n");
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq,
+ sx_common_irq_handler,
+ sx_common_irq_thread_handler,
+ IRQF_ONESHOT,
+ "sx_event", indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "No IRQ\n");
+
+ data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->ops = &sx_common_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
+
+ ret = devm_iio_trigger_register(dev, data->trig);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ sx_common_trigger_handler,
+ &sx_common_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(sx_common_probe, SEMTECH_PROX);
+
+MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
+MODULE_DESCRIPTION("Common functions and structures for Semtech sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h
new file mode 100644
index 000000000000..5d3edeb75f4e
--- /dev/null
+++ b/drivers/iio/proximity/sx_common.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Code shared between most Semtech SAR sensor driver.
+ */
+
+#ifndef IIO_SX_COMMON_H
+#define IIO_SX_COMMON_H
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+struct device;
+struct i2c_client;
+struct regmap_config;
+struct sx_common_data;
+
+#define SX_COMMON_REG_IRQ_SRC 0x00
+
+#define SX_COMMON_MAX_NUM_CHANNELS 4
+static_assert(SX_COMMON_MAX_NUM_CHANNELS < BITS_PER_LONG);
+
+struct sx_common_reg_default {
+ u8 reg;
+ u8 def;
+};
+
+/**
+ * struct sx_common_ops: function pointers needed by common code
+ *
+ * List functions needed by common code to gather information or configure
+ * the sensor.
+ *
+ * @read_prox_data: Function to read raw proximity data.
+ * @check_whoami: Set device name based on whoami register.
+ * @init_compensation: Function to set initial compensation.
+ * @wait_for_sample: When there are no physical IRQ, function to wait for a
+ * sample to be ready.
+ * @get_default_reg: Populate the initial value for a given register.
+ */
+struct sx_common_ops {
+ int (*read_prox_data)(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, __be16 *val);
+ int (*check_whoami)(struct device *dev, struct iio_dev *indio_dev);
+ int (*init_compensation)(struct iio_dev *indio_dev);
+ int (*wait_for_sample)(struct sx_common_data *data);
+ const struct sx_common_reg_default *
+ (*get_default_reg)(struct device *dev, int idx,
+ struct sx_common_reg_default *reg_def);
+};
+
+/**
+ * struct sx_common_chip_info: Semtech Sensor private chip information
+ *
+ * @reg_stat: Main status register address.
+ * @reg_irq_msk: IRQ mask register address.
+ * @reg_enable_chan: Address to enable/disable channels.
+ * Each phase presented by the sensor is an IIO channel..
+ * @reg_reset: Reset register address.
+ * @mask_enable_chan: Mask over the channels bits in the enable channel
+ * register.
+ * @stat_offset: Offset to check phase status.
+ * @irq_msk_offset: Offset to enable interrupt in the IRQ mask
+ * register.
+ * @num_channels: Number of channels.
+ * @num_default_regs: Number of internal registers that can be configured.
+ *
+ * @ops: Private functions pointers.
+ * @iio_channels: Description of exposed iio channels.
+ * @num_iio_channels: Number of iio_channels.
+ * @iio_info: iio_info structure for this driver.
+ */
+struct sx_common_chip_info {
+ unsigned int reg_stat;
+ unsigned int reg_irq_msk;
+ unsigned int reg_enable_chan;
+ unsigned int reg_reset;
+
+ unsigned int mask_enable_chan;
+ unsigned int stat_offset;
+ unsigned int irq_msk_offset;
+ unsigned int num_channels;
+ int num_default_regs;
+
+ struct sx_common_ops ops;
+
+ const struct iio_chan_spec *iio_channels;
+ int num_iio_channels;
+ struct iio_info iio_info;
+};
+
+/**
+ * struct sx_common_data: Semtech Sensor private data structure.
+ *
+ * @chip_info: Structure defining sensor internals.
+ * @mutex: Serialize access to registers and channel configuration.
+ * @completion: completion object to wait for data acquisition.
+ * @client: I2C client structure.
+ * @trig: IIO trigger object.
+ * @regmap: Register map.
+ * @num_default_regs: Number of default registers to set at init.
+ * @supplies: Power supplies object.
+ * @chan_prox_stat: Last reading of the proximity status for each channel.
+ * We only send an event to user space when this changes.
+ * @trigger_enabled: True when the device trigger is enabled.
+ * @buffer: Buffer to store raw samples.
+ * @suspend_ctrl: Remember enabled channels and sample rate during suspend.
+ * @chan_read: Bit field for each raw channel enabled.
+ * @chan_event: Bit field for each event enabled.
+ */
+struct sx_common_data {
+ const struct sx_common_chip_info *chip_info;
+
+ struct mutex mutex;
+ struct completion completion;
+ struct i2c_client *client;
+ struct iio_trigger *trig;
+ struct regmap *regmap;
+
+ struct regulator_bulk_data supplies[2];
+ unsigned long chan_prox_stat;
+ bool trigger_enabled;
+
+ /* Ensure correct alignment of timestamp when present. */
+ struct {
+ __be16 channels[SX_COMMON_MAX_NUM_CHANNELS];
+ s64 ts __aligned(8);
+ } buffer;
+
+ unsigned int suspend_ctrl;
+ unsigned long chan_read;
+ unsigned long chan_event;
+};
+
+int sx_common_read_proximity(struct sx_common_data *data,
+ const struct iio_chan_spec *chan, int *val);
+
+int sx_common_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir);
+int sx_common_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state);
+
+int sx_common_probe(struct i2c_client *client,
+ const struct sx_common_chip_info *chip_info,
+ const struct regmap_config *regmap_config);
+
+/* 3 is the number of events defined by a single phase. */
+extern const struct iio_event_spec sx_common_events[3];
+
+#endif /* IIO_SX_COMMON_H */
diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c
index cf38144b6f95..661a79ea200d 100644
--- a/drivers/iio/proximity/vl53l0x-i2c.c
+++ b/drivers/iio/proximity/vl53l0x-i2c.c
@@ -226,7 +226,7 @@ static int vl53l0x_probe(struct i2c_client *client)
}
static const struct i2c_device_id vl53l0x_id[] = {
- { "vl53l0x", 0},
+ { "vl53l0x", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vl53l0x_id);
diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c
index 1954322e43be..54840881259a 100644
--- a/drivers/iio/temperature/max31856.c
+++ b/drivers/iio/temperature/max31856.c
@@ -320,7 +320,7 @@ static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf)
fault = reg_val & faultbit;
- return sprintf(buf, "%d\n", fault);
+ return sysfs_emit(buf, "%d\n", fault);
}
static ssize_t show_fault_ovuv(struct device *dev,
@@ -344,7 +344,7 @@ static ssize_t show_filter(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct max31856_data *data = iio_priv(indio_dev);
- return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60);
+ return sysfs_emit(buf, "%d\n", data->filter_50hz ? 50 : 60);
}
static ssize_t set_filter(struct device *dev,
diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c
index 4c8d6e6cf677..86c3f3509a26 100644
--- a/drivers/iio/temperature/max31865.c
+++ b/drivers/iio/temperature/max31865.c
@@ -208,7 +208,7 @@ static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf)
fault = data->buf[0] & faultbit;
- return sprintf(buf, "%d\n", fault);
+ return sysfs_emit(buf, "%d\n", fault);
}
static ssize_t show_fault_ovuv(struct device *dev,
@@ -225,7 +225,7 @@ static ssize_t show_filter(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct max31865_data *data = iio_priv(indio_dev);
- return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60);
+ return sysfs_emit(buf, "%d\n", data->filter_50hz ? 50 : 60);
}
static ssize_t set_filter(struct device *dev,
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
index 0297e215b61a..98c41cddc6f0 100644
--- a/drivers/iio/temperature/maxim_thermocouple.c
+++ b/drivers/iio/temperature/maxim_thermocouple.c
@@ -6,12 +6,11 @@
* Author: <matt.ranostay@konsulko.com>
*/
-#include <linux/module.h>
#include <linux/init.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index afcb10ea7c44..c253a5315988 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -600,7 +600,6 @@ static const struct of_device_id mlx90614_of_match[] = {
};
MODULE_DEVICE_TABLE(of, mlx90614_of_match);
-#ifdef CONFIG_PM_SLEEP
static int mlx90614_pm_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -630,9 +629,7 @@ static int mlx90614_pm_resume(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM
static int mlx90614_pm_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
@@ -648,19 +645,18 @@ static int mlx90614_pm_runtime_resume(struct device *dev)
return mlx90614_wakeup(data);
}
-#endif
static const struct dev_pm_ops mlx90614_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
- SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
- mlx90614_pm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
+ RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
+ mlx90614_pm_runtime_resume, NULL)
};
static struct i2c_driver mlx90614_driver = {
.driver = {
.name = "mlx90614",
.of_match_table = mlx90614_of_match,
- .pm = &mlx90614_pm_ops,
+ .pm = pm_ptr(&mlx90614_pm_ops),
},
.probe = mlx90614_probe,
.remove = mlx90614_remove,
diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c
index 608ccb1d8bc8..7ee7ff8047a4 100644
--- a/drivers/iio/temperature/mlx90632.c
+++ b/drivers/iio/temperature/mlx90632.c
@@ -13,9 +13,9 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/limits.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/math64.h>
-#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c
index e4943a0bc9aa..706a760f30b4 100644
--- a/drivers/iio/temperature/tmp006.c
+++ b/drivers/iio/temperature/tmp006.c
@@ -261,7 +261,6 @@ static int tmp006_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int tmp006_suspend(struct device *dev)
{
return tmp006_power(dev, false);
@@ -271,9 +270,8 @@ static int tmp006_resume(struct device *dev)
{
return tmp006_power(dev, true);
}
-#endif
-static SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume);
static const struct i2c_device_id tmp006_id[] = {
{ "tmp006", 0 },
@@ -284,7 +282,7 @@ MODULE_DEVICE_TABLE(i2c, tmp006_id);
static struct i2c_driver tmp006_driver = {
.driver = {
.name = "tmp006",
- .pm = &tmp006_pm_ops,
+ .pm = pm_sleep_ptr(&tmp006_pm_ops),
},
.probe = tmp006_probe,
.id_table = tmp006_id,
diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c
index b422371a4674..f3420d8a0e35 100644
--- a/drivers/iio/temperature/tmp007.c
+++ b/drivers/iio/temperature/tmp007.c
@@ -537,7 +537,6 @@ static int tmp007_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int tmp007_suspend(struct device *dev)
{
struct tmp007_data *data = iio_priv(i2c_get_clientdata(
@@ -554,9 +553,8 @@ static int tmp007_resume(struct device *dev)
return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
data->config | TMP007_CONFIG_CONV_EN);
}
-#endif
-static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume);
static const struct of_device_id tmp007_of_match[] = {
{ .compatible = "ti,tmp007", },
@@ -574,7 +572,7 @@ static struct i2c_driver tmp007_driver = {
.driver = {
.name = "tmp007",
.of_match_table = tmp007_of_match,
- .pm = &tmp007_pm_ops,
+ .pm = pm_sleep_ptr(&tmp007_pm_ops),
},
.probe = tmp007_probe,
.id_table = tmp007_id,
diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c
index bbfbad9a8767..60d58ec5b063 100644
--- a/drivers/iio/temperature/tsys01.c
+++ b/drivers/iio/temperature/tsys01.c
@@ -233,3 +233,4 @@ MODULE_DESCRIPTION("Measurement-Specialties tsys01 temperature driver");
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS);
diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c
index fc96e5f9d3fc..49c275e4f510 100644
--- a/drivers/iio/temperature/tsys02d.c
+++ b/drivers/iio/temperature/tsys02d.c
@@ -187,3 +187,4 @@ MODULE_DESCRIPTION("Measurement-Specialties tsys02d temperature driver");
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS);
diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig
index 679a7794af20..56ca0ad7e77a 100644
--- a/drivers/iio/test/Kconfig
+++ b/drivers/iio/test/Kconfig
@@ -4,6 +4,16 @@
#
# Keep in alphabetical order
+config IIO_RESCALE_KUNIT_TEST
+ bool "Test IIO rescale conversion functions"
+ depends on KUNIT=y && !IIO_RESCALE
+ default KUNIT_ALL_TESTS
+ help
+ If you want to run tests on the iio-rescale code say Y here.
+
+ This takes advantage of ARCH=um to run tests and should be used by
+ developers to tests their changes to the rescaling logic.
+
config IIO_TEST_FORMAT
bool "Test IIO formatting functions"
depends on KUNIT=y
diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile
index 467519a2027e..f15ae0a6394f 100644
--- a/drivers/iio/test/Makefile
+++ b/drivers/iio/test/Makefile
@@ -4,5 +4,6 @@
#
# Keep in alphabetical order
+obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) += iio-test-rescale.o ../afe/iio-rescale.o
obj-$(CONFIG_IIO_TEST_FORMAT) += iio-test-format.o
CFLAGS_iio-test-format.o += $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c
new file mode 100644
index 000000000000..0b6699bfd553
--- /dev/null
+++ b/drivers/iio/test/iio-test-rescale.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Kunit tests for IIO rescale conversions
+ *
+ * Copyright (c) 2021 Liam Beguin <liambeguin@gmail.com>
+ */
+
+#include <linux/gcd.h>
+#include <linux/overflow.h>
+
+#include <linux/iio/afe/rescale.h>
+#include <linux/iio/iio.h>
+
+#include <kunit/test.h>
+
+struct rescale_tc_data {
+ const char *name;
+
+ const s32 numerator;
+ const s32 denominator;
+ const s32 offset;
+
+ const int schan_val;
+ const int schan_val2;
+ const int schan_off;
+ const int schan_scale_type;
+
+ const char *expected;
+ const char *expected_off;
+};
+
+const struct rescale_tc_data scale_cases[] = {
+ /*
+ * Typical use cases
+ */
+ {
+ .name = "typical IIO_VAL_INT, positive",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT,
+ .schan_val = 42,
+ .expected = "5210.918114143",
+ },
+ {
+ .name = "typical IIO_VAL_INT, negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT,
+ .schan_val = 42,
+ .expected = "-5210.918114143",
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL, positive",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 42,
+ .schan_val2 = 20,
+ .expected = "260.545905707",
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL, negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 42,
+ .schan_val2 = 20,
+ .expected = "-260.545905707",
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL_LOG2, positive",
+ .numerator = 42,
+ .denominator = 53,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 4096,
+ .schan_val2 = 16,
+ .expected = "0.049528301",
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL_LOG2, negative",
+ .numerator = -42,
+ .denominator = 53,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 4096,
+ .schan_val2 = 16,
+ .expected = "-0.049528301",
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_NANO, positive",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = 123456,
+ .expected = "1240.710106203",
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_NANO, negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = 123456,
+ .expected = "-1240.710106203",
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_MICRO, positive",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = 1234,
+ .expected = "1240.84789",
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_MICRO, negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = 1234,
+ .expected = "-1240.84789",
+ },
+ /*
+ * Use cases with small scales involving divisions
+ */
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 261/509 scaled by 90/1373754273",
+ .numerator = 261,
+ .denominator = 509,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 90,
+ .schan_val2 = 1373754273,
+ .expected = "0.000000033594",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 90/1373754273 scaled by 261/509",
+ .numerator = 90,
+ .denominator = 1373754273,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 261,
+ .schan_val2 = 509,
+ .expected = "0.000000033594",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 760/1373754273 scaled by 427/2727",
+ .numerator = 760,
+ .denominator = 1373754273,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 427,
+ .schan_val2 = 2727,
+ .expected = "0.000000086626",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 761/1373754273 scaled by 427/2727",
+ .numerator = 761,
+ .denominator = 1373754273,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 427,
+ .schan_val2 = 2727,
+ .expected = "0.000000086740",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 5/32768 scaled by 3/10000",
+ .numerator = 5,
+ .denominator = 32768,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 3,
+ .schan_val2 = 10000,
+ .expected = "0.0000000457763671875",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 0 < scale < 1",
+ .numerator = 6,
+ .denominator = 6,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 1,
+ .schan_val2 = 3,
+ .expected = "0.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, -1 < scale < 0",
+ .numerator = -6,
+ .denominator = 6,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 1,
+ .schan_val2 = 3,
+ .expected = "-0.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, 0 < scale < 2",
+ .numerator = 8,
+ .denominator = 2,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 1,
+ .schan_val2 = 3,
+ .expected = "1.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL, -2 < scale < 0",
+ .numerator = -8,
+ .denominator = 2,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 1,
+ .schan_val2 = 3,
+ .expected = "-1.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL_LOG2, 760/32768 scaled by 15/22",
+ .numerator = 760,
+ .denominator = 32768,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 15,
+ .schan_val2 = 22,
+ .expected = "0.000000082946",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL_LOG2, 761/32768 scaled by 15/22",
+ .numerator = 761,
+ .denominator = 32768,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 15,
+ .schan_val2 = 22,
+ .expected = "0.000000083055",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL_LOG2, 0 < scale < 1",
+ .numerator = 16,
+ .denominator = 3,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 1,
+ .schan_val2 = 4,
+ .expected = "0.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL_LOG2, -1 < scale < 0",
+ .numerator = -16,
+ .denominator = 3,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 1,
+ .schan_val2 = 4,
+ .expected = "-0.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL_LOG2, 0 < scale < 2",
+ .numerator = 8,
+ .denominator = 3,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 1,
+ .schan_val2 = 1,
+ .expected = "1.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_FRACTIONAL_LOG2, -2 < scale < 0",
+ .numerator = -8,
+ .denominator = 3,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 1,
+ .schan_val2 = 1,
+ .expected = "-1.3333333333333333",
+ },
+ {
+ .name = "small IIO_VAL_INT_PLUS_MICRO, positive",
+ .numerator = 1,
+ .denominator = 2,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 5,
+ .schan_val2 = 1234,
+ .expected = "2.500617",
+ },
+ {
+ .name = "small IIO_VAL_INT_PLUS_MICRO, negative",
+ .numerator = -1,
+ .denominator = 2,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 5,
+ .schan_val2 = 1234,
+ .expected = "-2.500617",
+ },
+ /*
+ * INT_PLUS_{MICRO,NANO} positive/negative corner cases
+ */
+ {
+ .name = "negative IIO_VAL_INT_PLUS_NANO, negative schan",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = -10,
+ .schan_val2 = 123456,
+ .expected = "-1240.710106203",
+ },
+ {
+ .name = "negative IIO_VAL_INT_PLUS_NANO, both negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = -10,
+ .schan_val2 = 123456,
+ .expected = "1240.710106203",
+ },
+ {
+ .name = "negative IIO_VAL_INT_PLUS_NANO, 3 negative",
+ .numerator = -1000000,
+ .denominator = -8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = -10,
+ .schan_val2 = 123456,
+ .expected = "-1240.710106203",
+ },
+ {
+ .name = "negative IIO_VAL_INT_PLUS_NANO, 4 negative",
+ .numerator = -1000000,
+ .denominator = -8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = -10,
+ .schan_val2 = -123456,
+ .expected = "-1240.710106203",
+ },
+ {
+ .name = "negative IIO_VAL_INT_PLUS_NANO, negative, *val = 0",
+ .numerator = 1,
+ .denominator = -10,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 0,
+ .schan_val2 = 123456789,
+ .expected = "-0.012345678",
+ },
+ /*
+ * INT_PLUS_{MICRO,NANO} decimal part overflow
+ */
+ {
+ .name = "decimal overflow IIO_VAL_INT_PLUS_NANO, positive",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .expected = "1256.01200856",
+ },
+ {
+ .name = "decimal overflow IIO_VAL_INT_PLUS_NANO, negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .expected = "-1256.01200856",
+ },
+ {
+ .name = "decimal overflow IIO_VAL_INT_PLUS_NANO, negative schan",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = -10,
+ .schan_val2 = 123456789,
+ .expected = "-1256.01200856",
+ },
+ {
+ .name = "decimal overflow IIO_VAL_INT_PLUS_MICRO, positive",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .expected = "16557.914267",
+ },
+ {
+ .name = "decimal overflow IIO_VAL_INT_PLUS_MICRO, negative",
+ .numerator = -1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .expected = "-16557.914267",
+ },
+ {
+ .name = "decimal overflow IIO_VAL_INT_PLUS_MICRO, negative schan",
+ .numerator = 1000000,
+ .denominator = 8060,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = -10,
+ .schan_val2 = 123456789,
+ .expected = "-16557.914267",
+ },
+ /*
+ * 32-bit overflow conditions
+ */
+ {
+ .name = "overflow IIO_VAL_FRACTIONAL, positive",
+ .numerator = 2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = S32_MAX,
+ .schan_val2 = 1,
+ .expected = "214748364.7",
+ },
+ {
+ .name = "overflow IIO_VAL_FRACTIONAL, negative",
+ .numerator = -2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = S32_MAX,
+ .schan_val2 = 1,
+ .expected = "-214748364.7",
+ },
+ {
+ .name = "overflow IIO_VAL_FRACTIONAL_LOG2, positive",
+ .numerator = S32_MAX,
+ .denominator = 4096,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 4096,
+ .schan_val2 = 16,
+ .expected = "32767.99998474121",
+ },
+ {
+ .name = "overflow IIO_VAL_FRACTIONAL_LOG2, negative",
+ .numerator = S32_MAX,
+ .denominator = 4096,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = -4096,
+ .schan_val2 = 16,
+ .expected = "-32767.99998474121",
+ },
+ {
+ .name = "overflow IIO_VAL_INT_PLUS_NANO, positive",
+ .numerator = 2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = S32_MAX,
+ .expected = "1.214748364",
+ },
+ {
+ .name = "overflow IIO_VAL_INT_PLUS_NANO, negative",
+ .numerator = -2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = S32_MAX,
+ .expected = "-1.214748364",
+ },
+ {
+ .name = "overflow IIO_VAL_INT_PLUS_NANO, negative schan",
+ .numerator = 2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = -10,
+ .schan_val2 = S32_MAX,
+ .expected = "-1.214748364",
+ },
+ {
+ .name = "overflow IIO_VAL_INT_PLUS_MICRO, positive",
+ .numerator = 2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = S32_MAX,
+ .expected = "215.748364",
+ },
+ {
+ .name = "overflow IIO_VAL_INT_PLUS_MICRO, negative",
+ .numerator = -2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = S32_MAX,
+ .expected = "-215.748364",
+ },
+ {
+ .name = "overflow IIO_VAL_INT_PLUS_MICRO, negative schan",
+ .numerator = 2,
+ .denominator = 20,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = -10,
+ .schan_val2 = S32_MAX,
+ .expected = "-215.748364",
+ },
+};
+
+const struct rescale_tc_data offset_cases[] = {
+ /*
+ * Typical use cases
+ */
+ {
+ .name = "typical IIO_VAL_INT, positive",
+ .offset = 1234,
+ .schan_scale_type = IIO_VAL_INT,
+ .schan_val = 123,
+ .schan_val2 = 0,
+ .schan_off = 14,
+ .expected_off = "24", /* 23.872 */
+ },
+ {
+ .name = "typical IIO_VAL_INT, negative",
+ .offset = -1234,
+ .schan_scale_type = IIO_VAL_INT,
+ .schan_val = 12,
+ .schan_val2 = 0,
+ .schan_off = 14,
+ .expected_off = "-88", /* -88.83333333333333 */
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL, positive",
+ .offset = 1234,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 12,
+ .schan_val2 = 34,
+ .schan_off = 14,
+ .expected_off = "3510", /* 3510.333333333333 */
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL, negative",
+ .offset = -1234,
+ .schan_scale_type = IIO_VAL_FRACTIONAL,
+ .schan_val = 12,
+ .schan_val2 = 34,
+ .schan_off = 14,
+ .expected_off = "-3482", /* -3482.333333333333 */
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL_LOG2, positive",
+ .offset = 1234,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 12,
+ .schan_val2 = 16,
+ .schan_off = 14,
+ .expected_off = "6739299", /* 6739299.333333333 */
+ },
+ {
+ .name = "typical IIO_VAL_FRACTIONAL_LOG2, negative",
+ .offset = -1234,
+ .schan_scale_type = IIO_VAL_FRACTIONAL_LOG2,
+ .schan_val = 12,
+ .schan_val2 = 16,
+ .schan_off = 14,
+ .expected_off = "-6739271", /* -6739271.333333333 */
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_NANO, positive",
+ .offset = 1234,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .schan_off = 14,
+ .expected_off = "135", /* 135.8951219647469 */
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_NANO, negative",
+ .offset = -1234,
+ .schan_scale_type = IIO_VAL_INT_PLUS_NANO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .schan_off = 14,
+ .expected_off = "-107", /* -107.89512196474689 */
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_MICRO, positive",
+ .offset = 1234,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .schan_off = 14,
+ .expected_off = "23", /* 23.246438560723952 */
+ },
+ {
+ .name = "typical IIO_VAL_INT_PLUS_MICRO, negative",
+ .offset = -12345,
+ .schan_scale_type = IIO_VAL_INT_PLUS_MICRO,
+ .schan_val = 10,
+ .schan_val2 = 123456789,
+ .schan_off = 14,
+ .expected_off = "-78", /* -78.50185091745313 */
+ },
+};
+
+static void case_to_desc(const struct rescale_tc_data *t, char *desc)
+{
+ strcpy(desc, t->name);
+}
+
+KUNIT_ARRAY_PARAM(iio_rescale_scale, scale_cases, case_to_desc);
+KUNIT_ARRAY_PARAM(iio_rescale_offset, offset_cases, case_to_desc);
+
+/**
+ * iio_str_to_nano() - Parse a fixed-point string to get an
+ * IIO_VAL_INT_PLUS_NANO value
+ * @str: The string to parse
+ * @nano: The number as an integer
+ *
+ * Returns 0 on success, or a negative error code if the string cound not be
+ * parsed.
+ */
+static int iio_str_to_nano(const char *str, s64 *nano)
+{
+ int tmp, tmp2;
+ int ret = 0;
+
+ /*
+ * iio_str_to_fixpoint() uses 10^8 here instead of 10^9 as fract_mult is
+ * the multiplier for the first decimal place.
+ */
+ ret = iio_str_to_fixpoint(str, 100000000, &tmp, &tmp2);
+ if (ret < 0)
+ return ret;
+
+ if (tmp < 0)
+ tmp2 *= -1;
+
+ *nano = (s64)tmp * 1000000000UL + tmp2;
+
+ return ret;
+}
+
+/**
+ * iio_test_relative_error_ppm() - Compute relative error (in parts-per-million)
+ * between two fixed-point strings
+ * @real_str: The real value as a string
+ * @exp_str: The expected value as a string
+ *
+ * Returns a negative error code if the strings cound not be parsed, or the
+ * relative error in parts-per-million.
+ */
+static int iio_test_relative_error_ppm(const char *real_str, const char *exp_str)
+{
+ s64 real, exp, err;
+ int ret;
+
+ ret = iio_str_to_nano(real_str, &real);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_str_to_nano(exp_str, &exp);
+ if (ret < 0)
+ return ret;
+
+ if (!exp) {
+ pr_err("Expected value is null, relative error is undefined\n");
+ return -EINVAL;
+ }
+
+ err = 1000000UL * abs(exp - real);
+
+ return (int)div64_u64(err, abs(exp));
+}
+
+static void iio_rescale_test_scale(struct kunit *test)
+{
+ struct rescale_tc_data *t = (struct rescale_tc_data *)test->param_value;
+ char *buff = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL);
+ struct rescale rescale;
+ int values[2];
+ int rel_ppm;
+ int ret;
+
+ rescale.numerator = t->numerator;
+ rescale.denominator = t->denominator;
+ rescale.offset = t->offset;
+ values[0] = t->schan_val;
+ values[1] = t->schan_val2;
+
+ ret = rescale_process_scale(&rescale, t->schan_scale_type,
+ &values[0], &values[1]);
+
+ ret = iio_format_value(buff, ret, 2, values);
+ KUNIT_EXPECT_EQ(test, (int)strlen(buff), ret);
+
+ rel_ppm = iio_test_relative_error_ppm(buff, t->expected);
+ KUNIT_EXPECT_GE_MSG(test, rel_ppm, 0, "failed to compute ppm\n");
+
+ KUNIT_EXPECT_EQ_MSG(test, rel_ppm, 0,
+ "\t real=%s"
+ "\texpected=%s\n",
+ buff, t->expected);
+}
+
+static void iio_rescale_test_offset(struct kunit *test)
+{
+ struct rescale_tc_data *t = (struct rescale_tc_data *)test->param_value;
+ char *buff_off = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL);
+ struct rescale rescale;
+ int values[2];
+ int ret;
+
+ rescale.numerator = t->numerator;
+ rescale.denominator = t->denominator;
+ rescale.offset = t->offset;
+ values[0] = t->schan_val;
+ values[1] = t->schan_val2;
+
+ ret = rescale_process_offset(&rescale, t->schan_scale_type,
+ t->schan_val, t->schan_val2, t->schan_off,
+ &values[0], &values[1]);
+
+ ret = iio_format_value(buff_off, ret, 2, values);
+ KUNIT_EXPECT_EQ(test, (int)strlen(buff_off), ret);
+
+ KUNIT_EXPECT_STREQ(test, strim(buff_off), t->expected_off);
+}
+
+static struct kunit_case iio_rescale_test_cases[] = {
+ KUNIT_CASE_PARAM(iio_rescale_test_scale, iio_rescale_scale_gen_params),
+ KUNIT_CASE_PARAM(iio_rescale_test_offset, iio_rescale_offset_gen_params),
+ {}
+};
+
+static struct kunit_suite iio_rescale_test_suite = {
+ .name = "iio-rescale",
+ .test_cases = iio_rescale_test_cases,
+};
+kunit_test_suite(iio_rescale_test_suite);
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index 8cef2f7452e8..7ecb69725b1d 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -38,7 +38,7 @@ config IIO_STM32_LPTIMER_TRIGGER
config IIO_STM32_TIMER_TRIGGER
tristate "STM32 Timer Trigger"
- depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
+ depends on (ARCH_STM32 && MFD_STM32_TIMERS) || COMPILE_TEST
help
Select this option to enable STM32 Timer Trigger
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
index 4f9461e1412c..3643c4afae67 100644
--- a/drivers/iio/trigger/stm32-timer-trigger.c
+++ b/drivers/iio/trigger/stm32-timer-trigger.c
@@ -11,9 +11,10 @@
#include <linux/iio/timer/stm32-timer-trigger.h>
#include <linux/iio/trigger.h>
#include <linux/mfd/stm32-timers.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
#define MAX_TRIGGERS 7
#define MAX_VALIDS 5
@@ -771,11 +772,11 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
unsigned int index;
int ret;
- if (of_property_read_u32(dev->of_node, "reg", &index))
- return -EINVAL;
+ ret = device_property_read_u32(dev, "reg", &index);
+ if (ret)
+ return ret;
- cfg = (const struct stm32_timer_trigger_cfg *)
- of_match_device(dev->driver->of_match_table, dev)->data;
+ cfg = device_get_match_data(dev);
if (index >= ARRAY_SIZE(triggers_table) ||
index >= cfg->num_valids_table)
@@ -827,7 +828,7 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev)
+static int stm32_timer_trigger_suspend(struct device *dev)
{
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
@@ -849,7 +850,7 @@ static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused stm32_timer_trigger_resume(struct device *dev)
+static int stm32_timer_trigger_resume(struct device *dev)
{
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
int ret;
@@ -875,9 +876,9 @@ static int __maybe_unused stm32_timer_trigger_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops,
- stm32_timer_trigger_suspend,
- stm32_timer_trigger_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops,
+ stm32_timer_trigger_suspend,
+ stm32_timer_trigger_resume);
static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = {
.valids_table = valids_table,
@@ -907,7 +908,7 @@ static struct platform_driver stm32_timer_trigger_driver = {
.driver = {
.name = "stm32-timer-trigger",
.of_match_table = stm32_trig_of_match,
- .pm = &stm32_timer_trigger_pm_ops,
+ .pm = pm_sleep_ptr(&stm32_timer_trigger_pm_ops),
},
};
module_platform_driver(stm32_timer_trigger_driver);
diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c
index c770951a909c..249ca25d1d55 100644
--- a/drivers/interconnect/imx/imx.c
+++ b/drivers/interconnect/imx/imx.c
@@ -25,6 +25,14 @@ struct imx_icc_node {
struct dev_pm_qos_request qos_req;
};
+static int imx_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
+{
+ *avg = 0;
+ *peak = 0;
+
+ return 0;
+}
+
static int imx_icc_node_set(struct icc_node *node)
{
struct device *dev = node->provider->dev;
@@ -241,6 +249,7 @@ int imx_icc_register(struct platform_device *pdev,
if (!provider)
return -ENOMEM;
provider->set = imx_icc_set;
+ provider->get_bw = imx_icc_get_bw;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c
index d188f3636e4c..f9c2d7d3100d 100644
--- a/drivers/interconnect/qcom/msm8939.c
+++ b/drivers/interconnect/qcom/msm8939.c
@@ -1301,19 +1301,11 @@ static struct qcom_icc_node *msm8939_snoc_mm_nodes[] = {
[SNOC_MM_INT_2] = &mm_int_2,
};
-static const struct regmap_config msm8939_snoc_mm_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x14080,
- .fast_io = true,
-};
-
static struct qcom_icc_desc msm8939_snoc_mm = {
.type = QCOM_ICC_NOC,
.nodes = msm8939_snoc_mm_nodes,
.num_nodes = ARRAY_SIZE(msm8939_snoc_mm_nodes),
- .regmap_cfg = &msm8939_snoc_mm_regmap_config,
+ .regmap_cfg = &msm8939_snoc_regmap_config,
.qos_offset = 0x7000,
};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0f5a49fc7c9e..41d2bb0ae23a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -259,6 +259,7 @@ config QCOM_FASTRPC
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
select DMA_SHARED_BUFFER
+ select QCOM_SCM
help
Provides a communication mechanism that allows for clients to
make remote method invocations across processor boundary to
@@ -470,6 +471,18 @@ config HISI_HIKEY_USB
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
+ help
+ This driver exposes a DICE reserved memory region to userspace via
+ a character device. The memory region contains Compound Device
+ Identifiers (CDIs) generated by firmware as an output of DICE
+ measured boot flow. Userspace can use CDIs for remote attestation
+ and sealing.
+
+ If unsure, say N.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a086197af544..70e800e9127f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
+obj-$(CONFIG_OPEN_DICE) += open-dice.o
diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c
index ad639ee85b2a..a16b99bdaa13 100644
--- a/drivers/misc/bcm-vk/bcm_vk_dev.c
+++ b/drivers/misc/bcm-vk/bcm_vk_dev.c
@@ -1633,7 +1633,6 @@ 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_VIPER), },
{ }
};
MODULE_DEVICE_TABLE(pci, bcm_vk_ids);
diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c
index de6d44a158bb..3f514d77a843 100644
--- a/drivers/misc/cardreader/alcor_pci.c
+++ b/drivers/misc/cardreader/alcor_pci.c
@@ -266,7 +266,7 @@ static int alcor_pci_probe(struct pci_dev *pdev,
if (!priv)
return -ENOMEM;
- ret = ida_simple_get(&alcor_pci_idr, 0, 0, GFP_KERNEL);
+ ret = ida_alloc(&alcor_pci_idr, GFP_KERNEL);
if (ret < 0)
return ret;
priv->id = ret;
@@ -280,7 +280,8 @@ static int alcor_pci_probe(struct pci_dev *pdev,
ret = pci_request_regions(pdev, DRV_NAME_ALCOR_PCI);
if (ret) {
dev_err(&pdev->dev, "Cannot request region\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error_free_ida;
}
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
@@ -324,6 +325,8 @@ static int alcor_pci_probe(struct pci_dev *pdev,
error_release_regions:
pci_release_regions(pdev);
+error_free_ida:
+ ida_free(&alcor_pci_idr, priv->id);
return ret;
}
@@ -337,7 +340,7 @@ static void alcor_pci_remove(struct pci_dev *pdev)
mfd_remove_devices(&pdev->dev);
- ida_simple_remove(&alcor_pci_idr, priv->id);
+ ida_free(&alcor_pci_idr, priv->id);
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
diff --git a/drivers/misc/cardreader/rtl8411.c b/drivers/misc/cardreader/rtl8411.c
index 4c5621b17a6f..06457e875a90 100644
--- a/drivers/misc/cardreader/rtl8411.c
+++ b/drivers/misc/cardreader/rtl8411.c
@@ -76,7 +76,7 @@ static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr)
map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg));
}
-static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
{
rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07);
}
diff --git a/drivers/misc/cardreader/rts5209.c b/drivers/misc/cardreader/rts5209.c
index 29f5414072bf..52b0a476ba51 100644
--- a/drivers/misc/cardreader/rts5209.c
+++ b/drivers/misc/cardreader/rts5209.c
@@ -47,7 +47,7 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr)
}
}
-static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
{
rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07);
}
diff --git a/drivers/misc/cardreader/rts5227.c b/drivers/misc/cardreader/rts5227.c
index 4bcfbc9afbac..d676cf63a966 100644
--- a/drivers/misc/cardreader/rts5227.c
+++ b/drivers/misc/cardreader/rts5227.c
@@ -72,6 +72,8 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr)
pci_read_config_dword(pdev, PCR_SETTING_REG2, &reg);
pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
+ if (CHK_PCI_PID(pcr, 0x522A))
+ pcr->rtd3_en = rtsx_reg_to_rtd3(reg);
if (rtsx_check_mmc_support(reg))
pcr->extra_caps |= EXTRA_CAPS_NO_MMC;
pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg);
@@ -171,6 +173,28 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr)
else
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x30, 0x00);
+ if (CHK_PCI_PID(pcr, 0x522A))
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_AUTOLOAD_CFG1,
+ CD_RESUME_EN_MASK, CD_RESUME_EN_MASK);
+
+ if (pcr->rtd3_en) {
+ if (CHK_PCI_PID(pcr, 0x522A)) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PM_CTRL3, 0x01, 0x01);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PME_FORCE_CTL, 0x30, 0x30);
+ } else {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x01, 0x01);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PME_FORCE_CTL, 0xFF, 0x33);
+ }
+ } else {
+ if (CHK_PCI_PID(pcr, 0x522A)) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PM_CTRL3, 0x01, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS522A_PME_FORCE_CTL, 0x30, 0x20);
+ } else {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PME_FORCE_CTL, 0xFF, 0x30);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x01, 0x00);
+ }
+ }
+
if (option->force_clkreq_0)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG,
FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW);
@@ -438,6 +462,28 @@ static int rts522a_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
return rtsx_pci_send_cmd(pcr, 100);
}
+static void rts522a_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
+{
+ /* Set relink_time to 0 */
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3,
+ RELINK_TIME_MASK, 0);
+
+ rtsx_pci_write_register(pcr, RTS522A_PM_CTRL3,
+ D3_DELINK_MODE_EN, D3_DELINK_MODE_EN);
+
+ if (!runtime) {
+ rtsx_pci_write_register(pcr, RTS522A_AUTOLOAD_CFG1,
+ CD_RESUME_EN_MASK, 0);
+ rtsx_pci_write_register(pcr, RTS522A_PM_CTRL3, 0x01, 0x00);
+ rtsx_pci_write_register(pcr, RTS522A_PME_FORCE_CTL, 0x30, 0x20);
+ }
+
+ rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN);
+}
+
+
static void rts522a_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active)
{
struct rtsx_cr_option *option = &pcr->option;
@@ -473,6 +519,7 @@ static const struct pcr_ops rts522a_pcr_ops = {
.card_power_on = rts5227_card_power_on,
.card_power_off = rts5227_card_power_off,
.switch_output_voltage = rts522a_switch_output_voltage,
+ .force_power_down = rts522a_force_power_down,
.cd_deglitch = NULL,
.conv_clk_and_div_n = NULL,
.set_l1off_cfg_sub_d0 = rts522a_set_l1off_cfg_sub_d0,
diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c
index ffc128278613..cfebad51d1d8 100644
--- a/drivers/misc/cardreader/rts5228.c
+++ b/drivers/misc/cardreader/rts5228.c
@@ -91,7 +91,7 @@ static int rts5228_optimize_phy(struct rtsx_pcr *pcr)
return rtsx_pci_write_phy_register(pcr, 0x07, 0x8F40);
}
-static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
{
/* Set relink_time to 0 */
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0);
@@ -102,6 +102,14 @@ static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3,
D3_DELINK_MODE_EN, D3_DELINK_MODE_EN);
+ if (!runtime) {
+ rtsx_pci_write_register(pcr, RTS5228_AUTOLOAD_CFG1,
+ CD_RESUME_EN_MASK, 0);
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00);
+ rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
+ }
+
rtsx_pci_write_register(pcr, FPDCTL,
SSC_POWER_DOWN, SSC_POWER_DOWN);
}
@@ -480,9 +488,18 @@ static int rts5228_extra_init_hw(struct rtsx_pcr *pcr)
FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH);
rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB);
- rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00);
- rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL,
- FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
+
+ if (pcr->rtd3_en) {
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x01);
+ rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE);
+ } else {
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00);
+ rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
+ }
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, 0x00);
return 0;
}
diff --git a/drivers/misc/cardreader/rts5229.c b/drivers/misc/cardreader/rts5229.c
index c748eaf1ec1f..b0edd8006d52 100644
--- a/drivers/misc/cardreader/rts5229.c
+++ b/drivers/misc/cardreader/rts5229.c
@@ -44,7 +44,7 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr)
map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg));
}
-static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
{
rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03);
}
diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c
index 53f3a1f45c4a..91d240dd68fa 100644
--- a/drivers/misc/cardreader/rts5249.c
+++ b/drivers/misc/cardreader/rts5249.c
@@ -74,7 +74,8 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr)
pci_read_config_dword(pdev, PCR_SETTING_REG2, &reg);
pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
- pcr->rtd3_en = rtsx_reg_to_rtd3_uhsii(reg);
+ if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A))
+ pcr->rtd3_en = rtsx_reg_to_rtd3_uhsii(reg);
if (rtsx_check_mmc_support(reg))
pcr->extra_caps |= EXTRA_CAPS_NO_MMC;
@@ -143,6 +144,27 @@ static int rts5249_init_from_hw(struct rtsx_pcr *pcr)
return 0;
}
+static void rts52xa_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
+{
+ /* Set relink_time to 0 */
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3,
+ RELINK_TIME_MASK, 0);
+
+ rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3,
+ D3_DELINK_MODE_EN, D3_DELINK_MODE_EN);
+
+ if (!runtime) {
+ rtsx_pci_write_register(pcr, RTS524A_AUTOLOAD_CFG1,
+ CD_RESUME_EN_MASK, 0);
+ rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, 0x01, 0x00);
+ rtsx_pci_write_register(pcr, RTS524A_PME_FORCE_CTL, 0x30, 0x20);
+ }
+
+ rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN);
+}
+
static void rts52xa_save_content_from_efuse(struct rtsx_pcr *pcr)
{
u8 cnt, sv;
@@ -281,8 +303,11 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr)
rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF);
- if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A))
+ if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) {
rtsx_pci_write_register(pcr, REG_VREF, PWD_SUSPND_EN, PWD_SUSPND_EN);
+ rtsx_pci_write_register(pcr, RTS524A_AUTOLOAD_CFG1,
+ CD_RESUME_EN_MASK, CD_RESUME_EN_MASK);
+ }
if (pcr->rtd3_en) {
if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) {
@@ -724,6 +749,7 @@ static const struct pcr_ops rts524a_pcr_ops = {
.card_power_on = rtsx_base_card_power_on,
.card_power_off = rtsx_base_card_power_off,
.switch_output_voltage = rtsx_base_switch_output_voltage,
+ .force_power_down = rts52xa_force_power_down,
.set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0,
};
@@ -841,6 +867,7 @@ static const struct pcr_ops rts525a_pcr_ops = {
.card_power_on = rts525a_card_power_on,
.card_power_off = rtsx_base_card_power_off,
.switch_output_voltage = rts525a_switch_output_voltage,
+ .force_power_down = rts52xa_force_power_down,
.set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0,
};
diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c
index 1fd4e0e50730..a77585ab0f30 100644
--- a/drivers/misc/cardreader/rts5261.c
+++ b/drivers/misc/cardreader/rts5261.c
@@ -91,7 +91,7 @@ static void rtsx5261_fetch_vendor_settings(struct rtsx_pcr *pcr)
pcr->sd30_drive_sel_3v3 = rts5261_reg_to_sd30_drive_sel_3v3(reg);
}
-static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
{
/* Set relink_time to 0 */
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0);
@@ -103,6 +103,24 @@ static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3,
D3_DELINK_MODE_EN, D3_DELINK_MODE_EN);
+ if (!runtime) {
+ rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG1,
+ CD_RESUME_EN_MASK, 0);
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00);
+ rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
+
+ } else {
+ rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE, 0);
+
+ rtsx_pci_write_register(pcr, RTS5261_FW_CTL,
+ RTS5261_INFORM_RTD3_COLD, RTS5261_INFORM_RTD3_COLD);
+ rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG4,
+ RTS5261_FORCE_PRSNT_LOW, RTS5261_FORCE_PRSNT_LOW);
+
+ }
+
rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL,
SSC_POWER_DOWN, SSC_POWER_DOWN);
}
@@ -536,9 +554,18 @@ static int rts5261_extra_init_hw(struct rtsx_pcr *pcr)
FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH);
rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB);
- rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00);
- rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
- FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
+
+ if (pcr->rtd3_en) {
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x01);
+ rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE);
+ } else {
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00);
+ rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL,
+ FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL);
+ }
+ rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, 0x00);
/* Clear Enter RTD3_cold Information*/
rtsx_pci_write_register(pcr, RTS5261_FW_CTL,
diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c
index 6ac509c1821c..2a2619e3c72c 100644
--- a/drivers/misc/cardreader/rtsx_pcr.c
+++ b/drivers/misc/cardreader/rtsx_pcr.c
@@ -152,20 +152,12 @@ void rtsx_pci_start_run(struct rtsx_pcr *pcr)
if (pcr->remove_pci)
return;
- if (pcr->rtd3_en)
- if (pcr->is_runtime_suspended) {
- pm_runtime_get(&(pcr->pci->dev));
- pcr->is_runtime_suspended = false;
- }
-
if (pcr->state != PDEV_STAT_RUN) {
pcr->state = PDEV_STAT_RUN;
if (pcr->ops->enable_auto_blink)
pcr->ops->enable_auto_blink(pcr);
rtsx_pm_full_on(pcr);
}
-
- mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200));
}
EXPORT_SYMBOL_GPL(rtsx_pci_start_run);
@@ -1062,73 +1054,7 @@ static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr)
return 0;
}
-static void rtsx_enable_aspm(struct rtsx_pcr *pcr)
-{
- if (pcr->ops->set_aspm)
- pcr->ops->set_aspm(pcr, true);
- else
- rtsx_comm_set_aspm(pcr, true);
-}
-
-static void rtsx_comm_pm_power_saving(struct rtsx_pcr *pcr)
-{
- struct rtsx_cr_option *option = &pcr->option;
-
- if (option->ltr_enabled) {
- u32 latency = option->ltr_l1off_latency;
-
- if (rtsx_check_dev_flag(pcr, L1_SNOOZE_TEST_EN))
- mdelay(option->l1_snooze_delay);
-
- rtsx_set_ltr_latency(pcr, latency);
- }
-
- if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN))
- rtsx_set_l1off_sub_cfg_d0(pcr, 0);
-
- rtsx_enable_aspm(pcr);
-}
-
-static void rtsx_pm_power_saving(struct rtsx_pcr *pcr)
-{
- rtsx_comm_pm_power_saving(pcr);
-}
-
-static void rtsx_pci_rtd3_work(struct work_struct *work)
-{
- struct delayed_work *dwork = to_delayed_work(work);
- struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, rtd3_work);
-
- pcr_dbg(pcr, "--> %s\n", __func__);
- if (!pcr->is_runtime_suspended)
- pm_runtime_put(&(pcr->pci->dev));
-}
-
-static void rtsx_pci_idle_work(struct work_struct *work)
-{
- struct delayed_work *dwork = to_delayed_work(work);
- struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work);
-
- pcr_dbg(pcr, "--> %s\n", __func__);
-
- mutex_lock(&pcr->pcr_mutex);
-
- pcr->state = PDEV_STAT_IDLE;
-
- if (pcr->ops->disable_auto_blink)
- pcr->ops->disable_auto_blink(pcr);
- if (pcr->ops->turn_off_led)
- pcr->ops->turn_off_led(pcr);
-
- rtsx_pm_power_saving(pcr);
-
- mutex_unlock(&pcr->pcr_mutex);
-
- if (pcr->rtd3_en)
- mod_delayed_work(system_wq, &pcr->rtd3_work, msecs_to_jiffies(10000));
-}
-
-static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+static void rtsx_base_force_power_down(struct rtsx_pcr *pcr)
{
/* Set relink_time to 0 */
rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0);
@@ -1142,7 +1068,7 @@ static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN);
}
-static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state)
+static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state, bool runtime)
{
if (pcr->ops->turn_off_led)
pcr->ops->turn_off_led(pcr);
@@ -1154,9 +1080,9 @@ static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state)
rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, pm_state);
if (pcr->ops->force_power_down)
- pcr->ops->force_power_down(pcr, pm_state);
+ pcr->ops->force_power_down(pcr, pm_state, runtime);
else
- rtsx_base_force_power_down(pcr, pm_state);
+ rtsx_base_force_power_down(pcr);
}
void rtsx_pci_enable_ocp(struct rtsx_pcr *pcr)
@@ -1598,7 +1524,6 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
pcr->card_inserted = 0;
pcr->card_removed = 0;
INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect);
- INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work);
pcr->msi_en = msi_en;
if (pcr->msi_en) {
@@ -1623,20 +1548,14 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
rtsx_pcr_cells[i].pdata_size = sizeof(*handle);
}
- if (pcr->rtd3_en) {
- INIT_DELAYED_WORK(&pcr->rtd3_work, rtsx_pci_rtd3_work);
- pm_runtime_allow(&pcidev->dev);
- pm_runtime_enable(&pcidev->dev);
- pcr->is_runtime_suspended = false;
- }
-
ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells,
ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL);
if (ret < 0)
goto free_slots;
- schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
+ pm_runtime_allow(&pcidev->dev);
+ pm_runtime_put(&pcidev->dev);
return 0;
@@ -1668,11 +1587,11 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)
struct pcr_handle *handle = pci_get_drvdata(pcidev);
struct rtsx_pcr *pcr = handle->pcr;
- if (pcr->rtd3_en)
- pm_runtime_get_noresume(&pcr->pci->dev);
-
pcr->remove_pci = true;
+ pm_runtime_get_sync(&pcidev->dev);
+ pm_runtime_forbid(&pcidev->dev);
+
/* Disable interrupts at the pcr level */
spin_lock_irq(&pcr->lock);
rtsx_pci_writel(pcr, RTSX_BIER, 0);
@@ -1680,9 +1599,6 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)
spin_unlock_irq(&pcr->lock);
cancel_delayed_work_sync(&pcr->carddet_work);
- cancel_delayed_work_sync(&pcr->idle_work);
- if (pcr->rtd3_en)
- cancel_delayed_work_sync(&pcr->rtd3_work);
mfd_remove_devices(&pcidev->dev);
@@ -1700,11 +1616,6 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)
idr_remove(&rtsx_pci_idr, pcr->id);
spin_unlock(&rtsx_pci_lock);
- if (pcr->rtd3_en) {
- pm_runtime_disable(&pcr->pci->dev);
- pm_runtime_put_noidle(&pcr->pci->dev);
- }
-
kfree(pcr->slots);
kfree(pcr);
kfree(handle);
@@ -1717,22 +1628,16 @@ static void rtsx_pci_remove(struct pci_dev *pcidev)
static int __maybe_unused rtsx_pci_suspend(struct device *dev_d)
{
struct pci_dev *pcidev = to_pci_dev(dev_d);
- struct pcr_handle *handle;
- struct rtsx_pcr *pcr;
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
- handle = pci_get_drvdata(pcidev);
- pcr = handle->pcr;
-
- cancel_delayed_work(&pcr->carddet_work);
- cancel_delayed_work(&pcr->idle_work);
+ cancel_delayed_work_sync(&pcr->carddet_work);
mutex_lock(&pcr->pcr_mutex);
- rtsx_pci_power_off(pcr, HOST_ENTER_S3);
-
- device_wakeup_disable(dev_d);
+ rtsx_pci_power_off(pcr, HOST_ENTER_S3, false);
mutex_unlock(&pcr->pcr_mutex);
return 0;
@@ -1741,15 +1646,12 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d)
static int __maybe_unused rtsx_pci_resume(struct device *dev_d)
{
struct pci_dev *pcidev = to_pci_dev(dev_d);
- struct pcr_handle *handle;
- struct rtsx_pcr *pcr;
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
int ret = 0;
dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
- handle = pci_get_drvdata(pcidev);
- pcr = handle->pcr;
-
mutex_lock(&pcr->pcr_mutex);
ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00);
@@ -1760,8 +1662,6 @@ static int __maybe_unused rtsx_pci_resume(struct device *dev_d)
if (ret)
goto out;
- schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
-
out:
mutex_unlock(&pcr->pcr_mutex);
return ret;
@@ -1769,16 +1669,46 @@ out:
#ifdef CONFIG_PM
+static void rtsx_enable_aspm(struct rtsx_pcr *pcr)
+{
+ if (pcr->ops->set_aspm)
+ pcr->ops->set_aspm(pcr, true);
+ else
+ rtsx_comm_set_aspm(pcr, true);
+}
+
+static void rtsx_comm_pm_power_saving(struct rtsx_pcr *pcr)
+{
+ struct rtsx_cr_option *option = &pcr->option;
+
+ if (option->ltr_enabled) {
+ u32 latency = option->ltr_l1off_latency;
+
+ if (rtsx_check_dev_flag(pcr, L1_SNOOZE_TEST_EN))
+ mdelay(option->l1_snooze_delay);
+
+ rtsx_set_ltr_latency(pcr, latency);
+ }
+
+ if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN))
+ rtsx_set_l1off_sub_cfg_d0(pcr, 0);
+
+ rtsx_enable_aspm(pcr);
+}
+
+static void rtsx_pm_power_saving(struct rtsx_pcr *pcr)
+{
+ rtsx_comm_pm_power_saving(pcr);
+}
+
static void rtsx_pci_shutdown(struct pci_dev *pcidev)
{
- struct pcr_handle *handle;
- struct rtsx_pcr *pcr;
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
- handle = pci_get_drvdata(pcidev);
- pcr = handle->pcr;
- rtsx_pci_power_off(pcr, HOST_ENTER_S1);
+ rtsx_pci_power_off(pcr, HOST_ENTER_S1, false);
pci_disable_device(pcidev);
free_irq(pcr->irq, (void *)pcr);
@@ -1786,47 +1716,63 @@ static void rtsx_pci_shutdown(struct pci_dev *pcidev)
pci_disable_msi(pcr->pci);
}
+static int rtsx_pci_runtime_idle(struct device *device)
+{
+ struct pci_dev *pcidev = to_pci_dev(device);
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
+
+ dev_dbg(device, "--> %s\n", __func__);
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ pcr->state = PDEV_STAT_IDLE;
+
+ if (pcr->ops->disable_auto_blink)
+ pcr->ops->disable_auto_blink(pcr);
+ if (pcr->ops->turn_off_led)
+ pcr->ops->turn_off_led(pcr);
+
+ rtsx_pm_power_saving(pcr);
+
+ mutex_unlock(&pcr->pcr_mutex);
+
+ if (pcr->rtd3_en)
+ pm_schedule_suspend(device, 10000);
+
+ return -EBUSY;
+}
+
static int rtsx_pci_runtime_suspend(struct device *device)
{
struct pci_dev *pcidev = to_pci_dev(device);
- struct pcr_handle *handle;
- struct rtsx_pcr *pcr;
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
- handle = pci_get_drvdata(pcidev);
- pcr = handle->pcr;
- dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+ dev_dbg(device, "--> %s\n", __func__);
- cancel_delayed_work(&pcr->carddet_work);
- cancel_delayed_work(&pcr->rtd3_work);
- cancel_delayed_work(&pcr->idle_work);
+ cancel_delayed_work_sync(&pcr->carddet_work);
mutex_lock(&pcr->pcr_mutex);
- rtsx_pci_power_off(pcr, HOST_ENTER_S3);
+ rtsx_pci_power_off(pcr, HOST_ENTER_S3, true);
mutex_unlock(&pcr->pcr_mutex);
- pcr->is_runtime_suspended = true;
-
return 0;
}
static int rtsx_pci_runtime_resume(struct device *device)
{
struct pci_dev *pcidev = to_pci_dev(device);
- struct pcr_handle *handle;
- struct rtsx_pcr *pcr;
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
- handle = pci_get_drvdata(pcidev);
- pcr = handle->pcr;
- dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+ dev_dbg(device, "--> %s\n", __func__);
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00);
- if (pcr->ops->fetch_vendor_settings)
- pcr->ops->fetch_vendor_settings(pcr);
-
rtsx_pci_init_hw(pcr);
if (pcr->slots[RTSX_SD_CARD].p_dev != NULL) {
@@ -1834,8 +1780,6 @@ static int rtsx_pci_runtime_resume(struct device *device)
pcr->slots[RTSX_SD_CARD].p_dev);
}
- schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
-
mutex_unlock(&pcr->pcr_mutex);
return 0;
}
@@ -1850,7 +1794,7 @@ static int rtsx_pci_runtime_resume(struct device *device)
static const struct dev_pm_ops rtsx_pci_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(rtsx_pci_suspend, rtsx_pci_resume)
- SET_RUNTIME_PM_OPS(rtsx_pci_runtime_suspend, rtsx_pci_runtime_resume, NULL)
+ SET_RUNTIME_PM_OPS(rtsx_pci_runtime_suspend, rtsx_pci_runtime_resume, rtsx_pci_runtime_idle)
};
static struct pci_driver rtsx_pci_driver = {
diff --git a/drivers/misc/cardreader/rtsx_pcr.h b/drivers/misc/cardreader/rtsx_pcr.h
index daf057c4eea6..37d1f316ae17 100644
--- a/drivers/misc/cardreader/rtsx_pcr.h
+++ b/drivers/misc/cardreader/rtsx_pcr.h
@@ -15,6 +15,8 @@
#define MIN_DIV_N_PCR 80
#define MAX_DIV_N_PCR 208
+#define RTS522A_PME_FORCE_CTL 0xFF78
+#define RTS522A_AUTOLOAD_CFG1 0xFF7C
#define RTS522A_PM_CTRL3 0xFF7E
#define RTS524A_PME_FORCE_CTL 0xFF78
@@ -25,6 +27,7 @@
#define REG_EFUSE_POWEROFF 0x00
#define RTS5250_CLK_CFG3 0xFF79
#define RTS525A_CFG_MEM_PD 0xF0
+#define RTS524A_AUTOLOAD_CFG1 0xFF7C
#define RTS524A_PM_CTRL3 0xFF7E
#define RTS525A_BIOS_CFG 0xFF2D
#define RTS525A_LOAD_BIOS_FLAG 0x01
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index bee727ed98db..91f96abbb3f9 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -309,7 +309,7 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
u32 val;
int err;
- strncpy(chip->name, "at25", sizeof(chip->name));
+ strscpy(chip->name, "at25", sizeof(chip->name));
err = device_property_read_u32(dev, "size", &val);
if (err)
@@ -370,7 +370,7 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
u8 id[FM25_ID_LEN];
int i;
- strncpy(chip->name, "fm25", sizeof(chip->name));
+ strscpy(chip->name, "fm25", sizeof(chip->name));
/* Get ID of chip */
fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN);
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index d60b176ffa95..d80ada8cac09 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -17,6 +17,7 @@
#include <linux/rpmsg.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/qcom_scm.h>
#include <uapi/misc/fastrpc.h>
#define ADSP_DOMAIN_ID (0)
@@ -25,16 +26,22 @@
#define CDSP_DOMAIN_ID (3)
#define FASTRPC_DEV_MAX 4 /* adsp, mdsp, slpi, cdsp*/
#define FASTRPC_MAX_SESSIONS 13 /*12 compute, 1 cpz*/
+#define FASTRPC_MAX_VMIDS 16
#define FASTRPC_ALIGN 128
#define FASTRPC_MAX_FDLIST 16
#define FASTRPC_MAX_CRCLIST 64
#define FASTRPC_PHYS(p) ((p) & 0xffffffff)
#define FASTRPC_CTX_MAX (256)
#define FASTRPC_INIT_HANDLE 1
+#define FASTRPC_DSP_UTILITIES_HANDLE 2
#define FASTRPC_CTXID_MASK (0xFF0)
#define INIT_FILELEN_MAX (2 * 1024 * 1024)
#define FASTRPC_DEVICE_NAME "fastrpc"
#define ADSP_MMAP_ADD_PAGES 0x1000
+#define DSP_UNSUPPORTED_API (0x80000414)
+/* MAX NUMBER of DSP ATTRIBUTES SUPPORTED */
+#define FASTRPC_MAX_DSP_ATTRIBUTES (256)
+#define FASTRPC_MAX_DSP_ATTRIBUTES_LEN (sizeof(u32) * FASTRPC_MAX_DSP_ATTRIBUTES)
/* Retrives number of input buffers from the scalars parameter */
#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff)
@@ -72,13 +79,15 @@
#define FASTRPC_RMID_INIT_CREATE 6
#define FASTRPC_RMID_INIT_CREATE_ATTR 7
#define FASTRPC_RMID_INIT_CREATE_STATIC 8
+#define FASTRPC_RMID_INIT_MEM_MAP 10
+#define FASTRPC_RMID_INIT_MEM_UNMAP 11
/* Protection Domain(PD) ids */
#define AUDIO_PD (0) /* also GUEST_OS PD? */
#define USER_PD (1)
#define SENSORS_PD (2)
-#define miscdev_to_cctx(d) container_of(d, struct fastrpc_channel_ctx, miscdev)
+#define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev)
static const char *domains[FASTRPC_DEV_MAX] = { "adsp", "mdsp",
"sdsp", "cdsp"};
@@ -92,9 +101,20 @@ struct fastrpc_invoke_buf {
u32 pgidx; /* index to start of contiguous region */
};
-struct fastrpc_remote_arg {
- u64 pv;
- u64 len;
+struct fastrpc_remote_dmahandle {
+ s32 fd; /* dma handle fd */
+ u32 offset; /* dma handle offset */
+ u32 len; /* dma handle length */
+};
+
+struct fastrpc_remote_buf {
+ u64 pv; /* buffer pointer */
+ u64 len; /* length of buffer */
+};
+
+union fastrpc_remote_arg {
+ struct fastrpc_remote_buf buf;
+ struct fastrpc_remote_dmahandle dma;
};
struct fastrpc_mmap_rsp_msg {
@@ -108,12 +128,29 @@ struct fastrpc_mmap_req_msg {
s32 num;
};
+struct fastrpc_mem_map_req_msg {
+ s32 pgid;
+ s32 fd;
+ s32 offset;
+ u32 flags;
+ u64 vaddrin;
+ s32 num;
+ s32 data_len;
+};
+
struct fastrpc_munmap_req_msg {
s32 pgid;
u64 vaddr;
u64 size;
};
+struct fastrpc_mem_unmap_req_msg {
+ s32 pgid;
+ s32 fd;
+ u64 vaddrin;
+ u64 len;
+};
+
struct fastrpc_msg {
int pid; /* process group id */
int tid; /* thread id */
@@ -170,6 +207,8 @@ struct fastrpc_map {
u64 size;
void *va;
u64 len;
+ u64 raddr;
+ u32 attr;
struct kref refcount;
};
@@ -189,7 +228,7 @@ struct fastrpc_invoke_ctx {
struct work_struct put_work;
struct fastrpc_msg msg;
struct fastrpc_user *fl;
- struct fastrpc_remote_arg *rpra;
+ union fastrpc_remote_arg *rpra;
struct fastrpc_map **maps;
struct fastrpc_buf *buf;
struct fastrpc_invoke_args *args;
@@ -207,13 +246,28 @@ struct fastrpc_session_ctx {
struct fastrpc_channel_ctx {
int domain_id;
int sesscount;
+ int vmcount;
+ u32 perms;
+ struct qcom_scm_vmperm vmperms[FASTRPC_MAX_VMIDS];
struct rpmsg_device *rpdev;
struct fastrpc_session_ctx session[FASTRPC_MAX_SESSIONS];
spinlock_t lock;
struct idr ctx_idr;
struct list_head users;
- struct miscdevice miscdev;
struct kref refcount;
+ /* Flag if dsp attributes are cached */
+ bool valid_attributes;
+ u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES];
+ struct fastrpc_device *secure_fdevice;
+ struct fastrpc_device *fdevice;
+ bool secure;
+ bool unsigned_support;
+};
+
+struct fastrpc_device {
+ struct fastrpc_channel_ctx *cctx;
+ struct miscdevice miscdev;
+ bool secure;
};
struct fastrpc_user {
@@ -228,6 +282,7 @@ struct fastrpc_user {
int tgid;
int pd;
+ bool is_secure_dev;
/* Lock for lists */
spinlock_t lock;
/* lock for allocations */
@@ -241,6 +296,20 @@ static void fastrpc_free_map(struct kref *ref)
map = container_of(ref, struct fastrpc_map, refcount);
if (map->table) {
+ if (map->attr & FASTRPC_ATTR_SECUREMAP) {
+ struct qcom_scm_vmperm perm;
+ int err = 0;
+
+ perm.vmid = QCOM_SCM_VMID_HLOS;
+ perm.perm = QCOM_SCM_PERM_RWX;
+ err = qcom_scm_assign_mem(map->phys, map->size,
+ &(map->fl->cctx->vmperms[0].vmid), &perm, 1);
+ if (err) {
+ dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
+ map->phys, map->size, err);
+ return;
+ }
+ }
dma_buf_unmap_attachment(map->attach, map->table,
DMA_BIDIRECTIONAL);
dma_buf_detach(map->buf, map->attach);
@@ -262,7 +331,8 @@ static void fastrpc_map_get(struct fastrpc_map *map)
kref_get(&map->refcount);
}
-static int fastrpc_map_find(struct fastrpc_user *fl, int fd,
+
+static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
struct fastrpc_map **ppmap)
{
struct fastrpc_map *map = NULL;
@@ -270,7 +340,6 @@ static int fastrpc_map_find(struct fastrpc_user *fl, int fd,
mutex_lock(&fl->mutex);
list_for_each_entry(map, &fl->maps, node) {
if (map->fd == fd) {
- fastrpc_map_get(map);
*ppmap = map;
mutex_unlock(&fl->mutex);
return 0;
@@ -281,6 +350,17 @@ static int fastrpc_map_find(struct fastrpc_user *fl, int fd,
return -ENOENT;
}
+static int fastrpc_map_find(struct fastrpc_user *fl, int fd,
+ struct fastrpc_map **ppmap)
+{
+ int ret = fastrpc_map_lookup(fl, fd, ppmap);
+
+ if (!ret)
+ fastrpc_map_get(*ppmap);
+
+ return ret;
+}
+
static void fastrpc_buf_free(struct fastrpc_buf *buf)
{
dma_free_coherent(buf->dev, buf->size, buf->virt,
@@ -353,7 +433,7 @@ static void fastrpc_context_free(struct kref *ref)
ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount);
cctx = ctx->cctx;
- for (i = 0; i < ctx->nscalars; i++)
+ for (i = 0; i < ctx->nbufs; i++)
fastrpc_map_put(ctx->maps[i]);
if (ctx->buf)
@@ -617,7 +697,7 @@ static const struct dma_buf_ops fastrpc_dma_buf_ops = {
};
static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
- u64 len, struct fastrpc_map **ppmap)
+ u64 len, u32 attr, struct fastrpc_map **ppmap)
{
struct fastrpc_session_ctx *sess = fl->sctx;
struct fastrpc_map *map = NULL;
@@ -659,6 +739,22 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
map->len = len;
kref_init(&map->refcount);
+ if (attr & FASTRPC_ATTR_SECUREMAP) {
+ /*
+ * If subsystem VMIDs are defined in DTSI, then do
+ * hyp_assign from HLOS to those VM(s)
+ */
+ unsigned int perms = BIT(QCOM_SCM_VMID_HLOS);
+
+ map->attr = attr;
+ err = qcom_scm_assign_mem(map->phys, (u64)map->size, &perms,
+ fl->cctx->vmperms, fl->cctx->vmcount);
+ if (err) {
+ dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d",
+ map->phys, map->size, err);
+ goto map_err;
+ }
+ }
spin_lock(&fl->lock);
list_add_tail(&map->node, &fl->maps);
spin_unlock(&fl->lock);
@@ -682,7 +778,7 @@ get_err:
* >>>>>> START of METADATA <<<<<<<<<
* +---------------------------------+
* | Arguments |
- * | type:(struct fastrpc_remote_arg)|
+ * | type:(union fastrpc_remote_arg)|
* | (0 - N) |
* +---------------------------------+
* | Invoke Buffer list |
@@ -707,7 +803,7 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx)
{
int size = 0;
- size = (sizeof(struct fastrpc_remote_arg) +
+ size = (sizeof(struct fastrpc_remote_buf) +
sizeof(struct fastrpc_invoke_buf) +
sizeof(struct fastrpc_phy_page)) * ctx->nscalars +
sizeof(u64) * FASTRPC_MAX_FDLIST +
@@ -743,16 +839,13 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx)
int i, err;
for (i = 0; i < ctx->nscalars; ++i) {
- /* Make sure reserved field is set to 0 */
- if (ctx->args[i].reserved)
- return -EINVAL;
if (ctx->args[i].fd == 0 || ctx->args[i].fd == -1 ||
ctx->args[i].length == 0)
continue;
err = fastrpc_map_create(ctx->fl, ctx->args[i].fd,
- ctx->args[i].length, &ctx->maps[i]);
+ ctx->args[i].length, ctx->args[i].attr, &ctx->maps[i]);
if (err) {
dev_err(dev, "Error Creating map %d\n", err);
return -EINVAL;
@@ -762,10 +855,20 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx)
return 0;
}
+static struct fastrpc_invoke_buf *fastrpc_invoke_buf_start(union fastrpc_remote_arg *pra, int len)
+{
+ return (struct fastrpc_invoke_buf *)(&pra[len]);
+}
+
+static struct fastrpc_phy_page *fastrpc_phy_page_start(struct fastrpc_invoke_buf *buf, int len)
+{
+ return (struct fastrpc_phy_page *)(&buf[len]);
+}
+
static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
{
struct device *dev = ctx->fl->sctx->dev;
- struct fastrpc_remote_arg *rpra;
+ union fastrpc_remote_arg *rpra;
struct fastrpc_invoke_buf *list;
struct fastrpc_phy_page *pages;
int inbufs, i, oix, err = 0;
@@ -789,9 +892,8 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
return err;
rpra = ctx->buf->virt;
- list = ctx->buf->virt + ctx->nscalars * sizeof(*rpra);
- pages = ctx->buf->virt + ctx->nscalars * (sizeof(*list) +
- sizeof(*rpra));
+ list = fastrpc_invoke_buf_start(rpra, ctx->nscalars);
+ pages = fastrpc_phy_page_start(list, ctx->nscalars);
args = (uintptr_t)ctx->buf->virt + metalen;
rlen = pkt_size - metalen;
ctx->rpra = rpra;
@@ -802,8 +904,8 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
i = ctx->olaps[oix].raix;
len = ctx->args[i].length;
- rpra[i].pv = 0;
- rpra[i].len = len;
+ rpra[i].buf.pv = 0;
+ rpra[i].buf.len = len;
list[i].num = len ? 1 : 0;
list[i].pgidx = i;
@@ -813,7 +915,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
if (ctx->maps[i]) {
struct vm_area_struct *vma = NULL;
- rpra[i].pv = (u64) ctx->args[i].ptr;
+ rpra[i].buf.pv = (u64) ctx->args[i].ptr;
pages[i].addr = ctx->maps[i]->phys;
mmap_read_lock(current->mm);
@@ -840,7 +942,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
if (rlen < mlen)
goto bail;
- rpra[i].pv = args - ctx->olaps[oix].offset;
+ rpra[i].buf.pv = args - ctx->olaps[oix].offset;
pages[i].addr = ctx->buf->phys -
ctx->olaps[oix].offset +
(pkt_size - rlen);
@@ -854,7 +956,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
}
if (i < inbufs && !ctx->maps[i]) {
- void *dst = (void *)(uintptr_t)rpra[i].pv;
+ void *dst = (void *)(uintptr_t)rpra[i].buf.pv;
void *src = (void *)(uintptr_t)ctx->args[i].ptr;
if (!kernel) {
@@ -870,12 +972,15 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
}
for (i = ctx->nbufs; i < ctx->nscalars; ++i) {
- rpra[i].pv = (u64) ctx->args[i].ptr;
- rpra[i].len = ctx->args[i].length;
list[i].num = ctx->args[i].length ? 1 : 0;
list[i].pgidx = i;
- pages[i].addr = ctx->maps[i]->phys;
- pages[i].size = ctx->maps[i]->size;
+ if (ctx->maps[i]) {
+ pages[i].addr = ctx->maps[i]->phys;
+ pages[i].size = ctx->maps[i]->size;
+ }
+ rpra[i].dma.fd = ctx->args[i].fd;
+ rpra[i].dma.len = ctx->args[i].length;
+ rpra[i].dma.offset = (u64) ctx->args[i].ptr;
}
bail:
@@ -888,16 +993,26 @@ bail:
static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx,
u32 kernel)
{
- struct fastrpc_remote_arg *rpra = ctx->rpra;
- int i, inbufs;
+ 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;
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]) {
- void *src = (void *)(uintptr_t)rpra[i].pv;
+ void *src = (void *)(uintptr_t)rpra[i].buf.pv;
void *dst = (void *)(uintptr_t)ctx->args[i].ptr;
- u64 len = rpra[i].len;
+ u64 len = rpra[i].buf.len;
if (!kernel) {
if (copy_to_user((void __user *)dst, src, len))
@@ -908,6 +1023,13 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx,
}
}
+ for (i = 0; i < FASTRPC_MAX_FDLIST; i++) {
+ if (!fdlist[i])
+ break;
+ if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap))
+ fastrpc_map_put(mmap);
+ }
+
return 0;
}
@@ -1016,6 +1138,24 @@ bail:
return err;
}
+static bool is_session_rejected(struct fastrpc_user *fl, bool unsigned_pd_request)
+{
+ /* Check if the device node is non-secure and channel is secure*/
+ if (!fl->is_secure_dev && fl->cctx->secure) {
+ /*
+ * Allow untrusted applications to offload only to Unsigned PD when
+ * channel is configured as secure and block untrusted apps on channel
+ * that does not support unsigned PD offload
+ */
+ if (!fl->cctx->unsigned_support || !unsigned_pd_request) {
+ dev_err(&fl->cctx->rpdev->dev, "Error: Untrusted application trying to offload to signed PD");
+ return true;
+ }
+ }
+
+ return false;
+}
+
static int fastrpc_init_create_process(struct fastrpc_user *fl,
char __user *argp)
{
@@ -1035,6 +1175,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl,
u32 siglen;
} inbuf;
u32 sc;
+ bool unsigned_module = false;
args = kcalloc(FASTRPC_CREATE_PROCESS_NARGS, sizeof(*args), GFP_KERNEL);
if (!args)
@@ -1045,6 +1186,14 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl,
goto err;
}
+ if (init.attrs & FASTRPC_MODE_UNSIGNED_MODULE)
+ unsigned_module = true;
+
+ if (is_session_rejected(fl, unsigned_module)) {
+ err = -ECONNREFUSED;
+ goto err;
+ }
+
if (init.filelen > INIT_FILELEN_MAX) {
err = -EINVAL;
goto err;
@@ -1059,7 +1208,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl,
fl->pd = USER_PD;
if (init.filelen && init.filefd) {
- err = fastrpc_map_create(fl, init.filefd, init.filelen, &map);
+ err = fastrpc_map_create(fl, init.filefd, init.filelen, 0, &map);
if (err)
goto err;
}
@@ -1168,7 +1317,6 @@ static int fastrpc_release_current_dsp_process(struct fastrpc_user *fl)
args[0].ptr = (u64)(uintptr_t) &tgid;
args[0].length = sizeof(tgid);
args[0].fd = -1;
- args[0].reserved = 0;
sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_RELEASE, 1, 0);
return fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE,
@@ -1220,10 +1368,14 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
static int fastrpc_device_open(struct inode *inode, struct file *filp)
{
- struct fastrpc_channel_ctx *cctx = miscdev_to_cctx(filp->private_data);
+ struct fastrpc_channel_ctx *cctx;
+ struct fastrpc_device *fdevice;
struct fastrpc_user *fl = NULL;
unsigned long flags;
+ fdevice = miscdev_to_fdevice(filp->private_data);
+ cctx = fdevice->cctx;
+
fl = kzalloc(sizeof(*fl), GFP_KERNEL);
if (!fl)
return -ENOMEM;
@@ -1240,6 +1392,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
INIT_LIST_HEAD(&fl->user);
fl->tgid = current->tgid;
fl->cctx = cctx;
+ fl->is_secure_dev = fdevice->secure;
fl->sctx = fastrpc_session_alloc(cctx);
if (!fl->sctx) {
@@ -1311,7 +1464,6 @@ static int fastrpc_init_attach(struct fastrpc_user *fl, int pd)
args[0].ptr = (u64)(uintptr_t) &tgid;
args[0].length = sizeof(tgid);
args[0].fd = -1;
- args[0].reserved = 0;
sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_ATTACH, 1, 0);
fl->pd = pd;
@@ -1349,6 +1501,107 @@ static int fastrpc_invoke(struct fastrpc_user *fl, char __user *argp)
return err;
}
+static int fastrpc_get_info_from_dsp(struct fastrpc_user *fl, uint32_t *dsp_attr_buf,
+ uint32_t dsp_attr_buf_len)
+{
+ struct fastrpc_invoke_args args[2] = { 0 };
+
+ /* Capability filled in userspace */
+ dsp_attr_buf[0] = 0;
+
+ args[0].ptr = (u64)(uintptr_t)&dsp_attr_buf_len;
+ args[0].length = sizeof(dsp_attr_buf_len);
+ args[0].fd = -1;
+ args[1].ptr = (u64)(uintptr_t)&dsp_attr_buf[1];
+ args[1].length = dsp_attr_buf_len;
+ args[1].fd = -1;
+ fl->pd = 1;
+
+ return fastrpc_internal_invoke(fl, true, FASTRPC_DSP_UTILITIES_HANDLE,
+ FASTRPC_SCALARS(0, 1, 1), args);
+}
+
+static int fastrpc_get_info_from_kernel(struct fastrpc_ioctl_capability *cap,
+ struct fastrpc_user *fl)
+{
+ struct fastrpc_channel_ctx *cctx = fl->cctx;
+ uint32_t attribute_id = cap->attribute_id;
+ uint32_t *dsp_attributes;
+ unsigned long flags;
+ uint32_t domain = cap->domain;
+ int err;
+
+ spin_lock_irqsave(&cctx->lock, flags);
+ /* check if we already have queried dsp for attributes */
+ if (cctx->valid_attributes) {
+ spin_unlock_irqrestore(&cctx->lock, flags);
+ goto done;
+ }
+ spin_unlock_irqrestore(&cctx->lock, flags);
+
+ dsp_attributes = kzalloc(FASTRPC_MAX_DSP_ATTRIBUTES_LEN, GFP_KERNEL);
+ if (!dsp_attributes)
+ return -ENOMEM;
+
+ err = fastrpc_get_info_from_dsp(fl, dsp_attributes, FASTRPC_MAX_DSP_ATTRIBUTES_LEN);
+ if (err == DSP_UNSUPPORTED_API) {
+ dev_info(&cctx->rpdev->dev,
+ "Warning: DSP capabilities not supported on domain: %d\n", domain);
+ kfree(dsp_attributes);
+ return -EOPNOTSUPP;
+ } else if (err) {
+ dev_err(&cctx->rpdev->dev, "Error: dsp information is incorrect err: %d\n", err);
+ kfree(dsp_attributes);
+ return err;
+ }
+
+ spin_lock_irqsave(&cctx->lock, flags);
+ memcpy(cctx->dsp_attributes, dsp_attributes, FASTRPC_MAX_DSP_ATTRIBUTES_LEN);
+ cctx->valid_attributes = true;
+ spin_unlock_irqrestore(&cctx->lock, flags);
+ kfree(dsp_attributes);
+done:
+ cap->capability = cctx->dsp_attributes[attribute_id];
+ return 0;
+}
+
+static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp)
+{
+ struct fastrpc_ioctl_capability cap = {0};
+ int err = 0;
+
+ if (copy_from_user(&cap, argp, sizeof(cap)))
+ return -EFAULT;
+
+ cap.capability = 0;
+ if (cap.domain >= FASTRPC_DEV_MAX) {
+ dev_err(&fl->cctx->rpdev->dev, "Error: Invalid domain id:%d, err:%d\n",
+ cap.domain, err);
+ return -ECHRNG;
+ }
+
+ /* Fastrpc Capablities does not support modem domain */
+ if (cap.domain == MDSP_DOMAIN_ID) {
+ dev_err(&fl->cctx->rpdev->dev, "Error: modem not supported %d\n", err);
+ return -ECHRNG;
+ }
+
+ if (cap.attribute_id >= FASTRPC_MAX_DSP_ATTRIBUTES) {
+ dev_err(&fl->cctx->rpdev->dev, "Error: invalid attribute: %d, err: %d\n",
+ cap.attribute_id, err);
+ return -EOVERFLOW;
+ }
+
+ err = fastrpc_get_info_from_kernel(&cap, fl);
+ if (err)
+ return err;
+
+ if (copy_to_user(argp, &cap.capability, sizeof(cap.capability)))
+ return -EFAULT;
+
+ return 0;
+}
+
static int fastrpc_req_munmap_impl(struct fastrpc_user *fl,
struct fastrpc_req_munmap *req)
{
@@ -1491,6 +1744,134 @@ err_invoke:
return err;
}
+static int fastrpc_req_mem_unmap_impl(struct fastrpc_user *fl, struct fastrpc_mem_unmap *req)
+{
+ struct fastrpc_invoke_args args[1] = { [0] = { 0 } };
+ struct fastrpc_map *map = NULL, *m;
+ struct fastrpc_mem_unmap_req_msg req_msg = { 0 };
+ int err = 0;
+ u32 sc;
+ struct device *dev = fl->sctx->dev;
+
+ spin_lock(&fl->lock);
+ list_for_each_entry_safe(map, m, &fl->maps, node) {
+ if ((req->fd < 0 || map->fd == req->fd) && (map->raddr == req->vaddr))
+ break;
+ map = NULL;
+ }
+
+ spin_unlock(&fl->lock);
+
+ if (!map) {
+ dev_err(dev, "map not in list\n");
+ return -EINVAL;
+ }
+
+ req_msg.pgid = fl->tgid;
+ req_msg.len = map->len;
+ req_msg.vaddrin = map->raddr;
+ req_msg.fd = map->fd;
+
+ args[0].ptr = (u64) (uintptr_t) &req_msg;
+ args[0].length = sizeof(req_msg);
+
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MEM_UNMAP, 1, 0);
+ err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc,
+ &args[0]);
+ fastrpc_map_put(map);
+ if (err)
+ dev_err(dev, "unmmap\tpt fd = %d, 0x%09llx error\n", map->fd, map->raddr);
+
+ return err;
+}
+
+static int fastrpc_req_mem_unmap(struct fastrpc_user *fl, char __user *argp)
+{
+ struct fastrpc_mem_unmap req;
+
+ if (copy_from_user(&req, argp, sizeof(req)))
+ return -EFAULT;
+
+ return fastrpc_req_mem_unmap_impl(fl, &req);
+}
+
+static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp)
+{
+ struct fastrpc_invoke_args args[4] = { [0 ... 3] = { 0 } };
+ struct fastrpc_mem_map_req_msg req_msg = { 0 };
+ struct fastrpc_mmap_rsp_msg rsp_msg = { 0 };
+ struct fastrpc_mem_unmap req_unmap = { 0 };
+ struct fastrpc_phy_page pages = { 0 };
+ struct fastrpc_mem_map req;
+ struct device *dev = fl->sctx->dev;
+ struct fastrpc_map *map = NULL;
+ int err;
+ u32 sc;
+
+ if (copy_from_user(&req, argp, sizeof(req)))
+ return -EFAULT;
+
+ /* create SMMU mapping */
+ err = fastrpc_map_create(fl, req.fd, req.length, 0, &map);
+ if (err) {
+ dev_err(dev, "failed to map buffer, fd = %d\n", req.fd);
+ return err;
+ }
+
+ req_msg.pgid = fl->tgid;
+ req_msg.fd = req.fd;
+ req_msg.offset = req.offset;
+ req_msg.vaddrin = req.vaddrin;
+ map->va = (void *) (uintptr_t) req.vaddrin;
+ req_msg.flags = req.flags;
+ req_msg.num = sizeof(pages);
+ req_msg.data_len = 0;
+
+ args[0].ptr = (u64) (uintptr_t) &req_msg;
+ args[0].length = sizeof(req_msg);
+
+ pages.addr = map->phys;
+ pages.size = map->size;
+
+ args[1].ptr = (u64) (uintptr_t) &pages;
+ args[1].length = sizeof(pages);
+
+ args[2].ptr = (u64) (uintptr_t) &pages;
+ args[2].length = 0;
+
+ args[3].ptr = (u64) (uintptr_t) &rsp_msg;
+ args[3].length = sizeof(rsp_msg);
+
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MEM_MAP, 3, 1);
+ err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc, &args[0]);
+ if (err) {
+ dev_err(dev, "mem mmap error, fd %d, vaddr %llx, size %lld\n",
+ req.fd, req.vaddrin, map->size);
+ goto err_invoke;
+ }
+
+ /* update the buffer to be able to deallocate the memory on the DSP */
+ map->raddr = rsp_msg.vaddr;
+
+ /* let the client know the address to use */
+ req.vaddrout = rsp_msg.vaddr;
+
+ if (copy_to_user((void __user *)argp, &req, sizeof(req))) {
+ /* unmap the memory and release the buffer */
+ req_unmap.vaddr = (uintptr_t) rsp_msg.vaddr;
+ req_unmap.length = map->size;
+ fastrpc_req_mem_unmap_impl(fl, &req_unmap);
+ return -EFAULT;
+ }
+
+ return 0;
+
+err_invoke:
+ fastrpc_map_put(map);
+
+ return err;
+}
+
static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -1520,6 +1901,15 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
case FASTRPC_IOCTL_MUNMAP:
err = fastrpc_req_munmap(fl, argp);
break;
+ case FASTRPC_IOCTL_MEM_MAP:
+ err = fastrpc_req_mem_map(fl, argp);
+ break;
+ case FASTRPC_IOCTL_MEM_UNMAP:
+ err = fastrpc_req_mem_unmap(fl, argp);
+ break;
+ case FASTRPC_IOCTL_GET_DSP_INFO:
+ err = fastrpc_get_dsp_info(fl, argp);
+ break;
default:
err = -ENOTTY;
break;
@@ -1615,12 +2005,41 @@ static struct platform_driver fastrpc_cb_driver = {
},
};
+static int fastrpc_device_register(struct device *dev, struct fastrpc_channel_ctx *cctx,
+ bool is_secured, const char *domain)
+{
+ struct fastrpc_device *fdev;
+ int err;
+
+ fdev = devm_kzalloc(dev, sizeof(*fdev), GFP_KERNEL);
+ if (!fdev)
+ return -ENOMEM;
+
+ fdev->secure = is_secured;
+ fdev->cctx = cctx;
+ fdev->miscdev.minor = MISC_DYNAMIC_MINOR;
+ fdev->miscdev.fops = &fastrpc_fops;
+ fdev->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "fastrpc-%s%s",
+ domain, is_secured ? "-secure" : "");
+ err = misc_register(&fdev->miscdev);
+ if (!err) {
+ if (is_secured)
+ cctx->secure_fdevice = fdev;
+ else
+ cctx->fdevice = fdev;
+ }
+
+ return err;
+}
+
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *rdev = &rpdev->dev;
struct fastrpc_channel_ctx *data;
- int i, err, domain_id = -1;
+ int i, err, domain_id = -1, vmcount;
const char *domain;
+ bool secure_dsp;
+ unsigned int vmids[FASTRPC_MAX_VMIDS];
err = of_property_read_string(rdev->of_node, "label", &domain);
if (err) {
@@ -1640,18 +2059,53 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
return -EINVAL;
}
+ vmcount = of_property_read_variable_u32_array(rdev->of_node,
+ "qcom,vmids", &vmids[0], 0, FASTRPC_MAX_VMIDS);
+ if (vmcount < 0)
+ vmcount = 0;
+ else if (!qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->miscdev.minor = MISC_DYNAMIC_MINOR;
- data->miscdev.name = devm_kasprintf(rdev, GFP_KERNEL, "fastrpc-%s",
- domains[domain_id]);
- data->miscdev.fops = &fastrpc_fops;
- err = misc_register(&data->miscdev);
- if (err) {
- kfree(data);
- return err;
+ if (vmcount) {
+ data->vmcount = vmcount;
+ data->perms = BIT(QCOM_SCM_VMID_HLOS);
+ for (i = 0; i < data->vmcount; i++) {
+ data->vmperms[i].vmid = vmids[i];
+ data->vmperms[i].perm = QCOM_SCM_PERM_RWX;
+ }
+ }
+
+ secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
+ data->secure = secure_dsp;
+
+ switch (domain_id) {
+ case ADSP_DOMAIN_ID:
+ case MDSP_DOMAIN_ID:
+ case SDSP_DOMAIN_ID:
+ /* Unsigned PD offloading is only supported on CDSP*/
+ data->unsigned_support = false;
+ err = fastrpc_device_register(rdev, data, secure_dsp, domains[domain_id]);
+ if (err)
+ goto fdev_error;
+ break;
+ case CDSP_DOMAIN_ID:
+ data->unsigned_support = true;
+ /* Create both device nodes so that we can allow both Signed and Unsigned PD */
+ err = fastrpc_device_register(rdev, data, true, domains[domain_id]);
+ if (err)
+ goto fdev_error;
+
+ err = fastrpc_device_register(rdev, data, false, domains[domain_id]);
+ if (err)
+ goto fdev_error;
+ break;
+ default:
+ err = -EINVAL;
+ goto fdev_error;
}
kref_init(&data->refcount);
@@ -1665,6 +2119,9 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
data->rpdev = rpdev;
return of_platform_populate(rdev->of_node, NULL, NULL, rdev);
+fdev_error:
+ kfree(data);
+ return err;
}
static void fastrpc_notify_users(struct fastrpc_user *user)
@@ -1688,7 +2145,12 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev)
fastrpc_notify_users(user);
spin_unlock_irqrestore(&cctx->lock, flags);
- misc_deregister(&cctx->miscdev);
+ if (cctx->fdevice)
+ misc_deregister(&cctx->fdevice->miscdev);
+
+ if (cctx->secure_fdevice)
+ misc_deregister(&cctx->secure_fdevice->miscdev);
+
of_platform_depopulate(&rpdev->dev);
cctx->rpdev = NULL;
diff --git a/drivers/misc/habanalabs/common/Makefile b/drivers/misc/habanalabs/common/Makefile
index 82c3824cad00..6ebe3c7001ff 100644
--- a/drivers/misc/habanalabs/common/Makefile
+++ b/drivers/misc/habanalabs/common/Makefile
@@ -11,4 +11,4 @@ HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \
common/command_buffer.o common/hw_queue.o common/irq.o \
common/sysfs.o common/hwmon.o common/memory.o \
common/command_submission.o common/firmware_if.o \
- common/state_dump.o common/hwmgr.o
+ common/state_dump.o
diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c
index 3c0ae07a2d80..a507110f6443 100644
--- a/drivers/misc/habanalabs/common/command_buffer.c
+++ b/drivers/misc/habanalabs/common/command_buffer.c
@@ -424,8 +424,8 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data)
{
union hl_cb_args *args = data;
struct hl_device *hdev = hpriv->hdev;
+ u64 handle = 0, device_va = 0;
enum hl_device_status status;
- u64 handle = 0, device_va;
u32 usage_cnt = 0;
int rc;
@@ -464,6 +464,8 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data)
args->in.flags,
&usage_cnt,
&device_va);
+ if (rc)
+ break;
memset(&args->out, 0, sizeof(args->out));
diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c
index 0a4ef13d9ac4..d93ef9f1c45c 100644
--- a/drivers/misc/habanalabs/common/command_submission.c
+++ b/drivers/misc/habanalabs/common/command_submission.c
@@ -14,6 +14,8 @@
#define HL_CS_FLAGS_TYPE_MASK (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT | \
HL_CS_FLAGS_COLLECTIVE_WAIT)
+#define MAX_TS_ITER_NUM 10
+
/**
* enum hl_cs_wait_status - cs wait status
* @CS_WAIT_STATUS_BUSY: cs was not completed yet
@@ -919,18 +921,21 @@ static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs)
complete_job(hdev, job);
}
-void hl_cs_rollback_all(struct hl_device *hdev)
+void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush)
{
int i;
struct hl_cs *cs, *tmp;
- flush_workqueue(hdev->sob_reset_wq);
+ if (!skip_wq_flush) {
+ flush_workqueue(hdev->ts_free_obj_wq);
- /* flush all completions before iterating over the CS mirror list in
- * order to avoid a race with the release functions
- */
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
- flush_workqueue(hdev->cq_wq[i]);
+ /* flush all completions before iterating over the CS mirror list in
+ * order to avoid a race with the release functions
+ */
+ for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
+ flush_workqueue(hdev->cq_wq[i]);
+
+ }
/* Make sure we don't have leftovers in the CS mirror list */
list_for_each_entry_safe(cs, tmp, &hdev->cs_mirror_list, mirror_node) {
@@ -948,13 +953,19 @@ void hl_cs_rollback_all(struct hl_device *hdev)
static void
wake_pending_user_interrupt_threads(struct hl_user_interrupt *interrupt)
{
- struct hl_user_pending_interrupt *pend;
+ struct hl_user_pending_interrupt *pend, *temp;
unsigned long flags;
spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_for_each_entry(pend, &interrupt->wait_list_head, wait_list_node) {
- pend->fence.error = -EIO;
- complete_all(&pend->fence.completion);
+ list_for_each_entry_safe(pend, temp, &interrupt->wait_list_head, wait_list_node) {
+ if (pend->ts_reg_info.ts_buff) {
+ list_del(&pend->wait_list_node);
+ hl_ts_put(pend->ts_reg_info.ts_buff);
+ hl_cb_put(pend->ts_reg_info.cq_cb);
+ } else {
+ pend->fence.error = -EIO;
+ complete_all(&pend->fence.completion);
+ }
}
spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
}
@@ -2063,13 +2074,16 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
idp = &ctx->sig_mgr.handles;
idr_for_each_entry(idp, encaps_sig_hdl, id) {
if (encaps_sig_hdl->cs_seq == signal_seq) {
- handle_found = true;
- /* get refcount to protect removing
- * this handle from idr, needed when
- * multiple wait cs are used with offset
+ /* get refcount to protect removing this handle from idr,
+ * needed when multiple wait cs are used with offset
* to wait on reserved encaps signals.
+ * Since kref_put of this handle is executed outside the
+ * current lock, it is possible that the handle refcount
+ * is 0 but it yet to be removed from the list. In this
+ * case need to consider the handle as not valid.
*/
- kref_get(&encaps_sig_hdl->refcount);
+ if (kref_get_unless_zero(&encaps_sig_hdl->refcount))
+ handle_found = true;
break;
}
}
@@ -2739,7 +2753,7 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
mcs_data.update_ts = false;
rc = hl_cs_poll_fences(&mcs_data, mcs_compl);
- if (mcs_data.completion_bitmap)
+ if (rc || mcs_data.completion_bitmap)
break;
/*
@@ -2854,64 +2868,174 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
return 0;
}
+static int ts_buff_get_kernel_ts_record(struct hl_ts_buff *ts_buff,
+ struct hl_cb *cq_cb,
+ u64 ts_offset, u64 cq_offset, u64 target_value,
+ spinlock_t *wait_list_lock,
+ struct hl_user_pending_interrupt **pend)
+{
+ struct hl_user_pending_interrupt *requested_offset_record =
+ (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
+ ts_offset;
+ struct hl_user_pending_interrupt *cb_last =
+ (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
+ (ts_buff->kernel_buff_size / sizeof(struct hl_user_pending_interrupt));
+ unsigned long flags, iter_counter = 0;
+ u64 current_cq_counter;
+
+ /* Validate ts_offset not exceeding last max */
+ if (requested_offset_record > cb_last) {
+ dev_err(ts_buff->hdev->dev, "Ts offset exceeds max CB offset(0x%llx)\n",
+ (u64)(uintptr_t)cb_last);
+ return -EINVAL;
+ }
+
+start_over:
+ spin_lock_irqsave(wait_list_lock, flags);
+
+ /* Unregister only if we didn't reach the target value
+ * since in this case there will be no handling in irq context
+ * and then it's safe to delete the node out of the interrupt list
+ * then re-use it on other interrupt
+ */
+ if (requested_offset_record->ts_reg_info.in_use) {
+ current_cq_counter = *requested_offset_record->cq_kernel_addr;
+ if (current_cq_counter < requested_offset_record->cq_target_value) {
+ list_del(&requested_offset_record->wait_list_node);
+ spin_unlock_irqrestore(wait_list_lock, flags);
+
+ hl_ts_put(requested_offset_record->ts_reg_info.ts_buff);
+ hl_cb_put(requested_offset_record->ts_reg_info.cq_cb);
+
+ dev_dbg(ts_buff->hdev->dev, "ts node removed from interrupt list now can re-use\n");
+ } else {
+ dev_dbg(ts_buff->hdev->dev, "ts node in middle of irq handling\n");
+
+ /* irq handling in the middle give it time to finish */
+ spin_unlock_irqrestore(wait_list_lock, flags);
+ usleep_range(1, 10);
+ if (++iter_counter == MAX_TS_ITER_NUM) {
+ dev_err(ts_buff->hdev->dev, "handling registration interrupt took too long!!\n");
+ return -EINVAL;
+ }
+
+ goto start_over;
+ }
+ } else {
+ spin_unlock_irqrestore(wait_list_lock, flags);
+ }
+
+ /* Fill up the new registration node info */
+ requested_offset_record->ts_reg_info.in_use = 1;
+ requested_offset_record->ts_reg_info.ts_buff = ts_buff;
+ requested_offset_record->ts_reg_info.cq_cb = cq_cb;
+ requested_offset_record->ts_reg_info.timestamp_kernel_addr =
+ (u64 *) ts_buff->user_buff_address + ts_offset;
+ requested_offset_record->cq_kernel_addr =
+ (u64 *) cq_cb->kernel_address + cq_offset;
+ requested_offset_record->cq_target_value = target_value;
+
+ *pend = requested_offset_record;
+
+ dev_dbg(ts_buff->hdev->dev, "Found available node in TS kernel CB(0x%llx)\n",
+ (u64)(uintptr_t)requested_offset_record);
+ return 0;
+}
+
static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
- struct hl_cb_mgr *cb_mgr, u64 timeout_us,
- u64 cq_counters_handle, u64 cq_counters_offset,
+ struct hl_cb_mgr *cb_mgr, struct hl_ts_mgr *ts_mgr,
+ u64 timeout_us, u64 cq_counters_handle, u64 cq_counters_offset,
u64 target_value, struct hl_user_interrupt *interrupt,
- u32 *status,
- u64 *timestamp)
+ bool register_ts_record, u64 ts_handle, u64 ts_offset,
+ u32 *status, u64 *timestamp)
{
+ u32 cq_patched_handle, ts_patched_handle;
struct hl_user_pending_interrupt *pend;
+ struct hl_ts_buff *ts_buff;
+ struct hl_cb *cq_cb;
unsigned long timeout, flags;
long completion_rc;
- struct hl_cb *cb;
int rc = 0;
- u32 handle;
timeout = hl_usecs64_to_jiffies(timeout_us);
hl_ctx_get(hdev, ctx);
- cq_counters_handle >>= PAGE_SHIFT;
- handle = (u32) cq_counters_handle;
-
- cb = hl_cb_get(hdev, cb_mgr, handle);
- if (!cb) {
- hl_ctx_put(ctx);
- return -EINVAL;
+ cq_patched_handle = lower_32_bits(cq_counters_handle >> PAGE_SHIFT);
+ cq_cb = hl_cb_get(hdev, cb_mgr, cq_patched_handle);
+ if (!cq_cb) {
+ rc = -EINVAL;
+ goto put_ctx;
}
- pend = kzalloc(sizeof(*pend), GFP_KERNEL);
- if (!pend) {
- hl_cb_put(cb);
- hl_ctx_put(ctx);
- return -ENOMEM;
- }
+ if (register_ts_record) {
+ dev_dbg(hdev->dev, "Timestamp registration: interrupt id: %u, ts offset: %llu, cq_offset: %llu\n",
+ interrupt->interrupt_id, ts_offset, cq_counters_offset);
- hl_fence_init(&pend->fence, ULONG_MAX);
+ ts_patched_handle = lower_32_bits(ts_handle >> PAGE_SHIFT);
+ ts_buff = hl_ts_get(hdev, ts_mgr, ts_patched_handle);
+ if (!ts_buff) {
+ rc = -EINVAL;
+ goto put_cq_cb;
+ }
- pend->cq_kernel_addr = (u64 *) cb->kernel_address + cq_counters_offset;
- pend->cq_target_value = target_value;
+ /* Find first available record */
+ rc = ts_buff_get_kernel_ts_record(ts_buff, cq_cb, ts_offset,
+ cq_counters_offset, target_value,
+ &interrupt->wait_list_lock, &pend);
+ if (rc)
+ goto put_ts_buff;
+ } else {
+ pend = kzalloc(sizeof(*pend), GFP_KERNEL);
+ if (!pend) {
+ rc = -ENOMEM;
+ goto put_cq_cb;
+ }
+ hl_fence_init(&pend->fence, ULONG_MAX);
+ pend->cq_kernel_addr = (u64 *) cq_cb->kernel_address + cq_counters_offset;
+ pend->cq_target_value = target_value;
+ }
+
+ spin_lock_irqsave(&interrupt->wait_list_lock, flags);
/* We check for completion value as interrupt could have been received
* before we added the node to the wait list
*/
if (*pend->cq_kernel_addr >= target_value) {
+ if (register_ts_record)
+ pend->ts_reg_info.in_use = 0;
+ spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
+
*status = HL_WAIT_CS_STATUS_COMPLETED;
- /* There was no interrupt, we assume the completion is now. */
- pend->fence.timestamp = ktime_get();
- }
- if (!timeout_us || (*status == HL_WAIT_CS_STATUS_COMPLETED))
+ if (register_ts_record) {
+ *pend->ts_reg_info.timestamp_kernel_addr = ktime_get_ns();
+ goto put_ts_buff;
+ } else {
+ pend->fence.timestamp = ktime_get();
+ goto set_timestamp;
+ }
+ } else if (!timeout_us) {
+ spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
+ *status = HL_WAIT_CS_STATUS_BUSY;
+ pend->fence.timestamp = ktime_get();
goto set_timestamp;
+ }
/* Add pending user interrupt to relevant list for the interrupt
- * handler to monitor
+ * handler to monitor.
+ * Note that we cannot have sorted list by target value,
+ * in order to shorten the list pass loop, since
+ * same list could have nodes for different cq counter handle.
*/
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head);
spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
+ if (register_ts_record) {
+ rc = *status = HL_WAIT_CS_STATUS_COMPLETED;
+ goto ts_registration_exit;
+ }
+
/* Wait for interrupt handler to signal completion */
completion_rc = wait_for_completion_interruptible_timeout(&pend->fence.completion,
timeout);
@@ -2932,23 +3056,41 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
rc = -EIO;
*status = HL_WAIT_CS_STATUS_ABORTED;
} else {
- dev_err_ratelimited(hdev->dev, "Waiting for interrupt ID %d timedout\n",
- interrupt->interrupt_id);
- rc = -ETIMEDOUT;
+ /* The wait has timed-out. We don't know anything beyond that
+ * because the workload wasn't submitted through the driver.
+ * Therefore, from driver's perspective, the workload is still
+ * executing.
+ */
+ rc = 0;
+ *status = HL_WAIT_CS_STATUS_BUSY;
}
- *status = HL_WAIT_CS_STATUS_BUSY;
}
}
+ /*
+ * We keep removing the node from list here, and not at the irq handler
+ * for completion timeout case. and if it's a registration
+ * for ts record, the node will be deleted in the irq handler after
+ * we reach the target value.
+ */
spin_lock_irqsave(&interrupt->wait_list_lock, flags);
list_del(&pend->wait_list_node);
spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
set_timestamp:
*timestamp = ktime_to_ns(pend->fence.timestamp);
-
kfree(pend);
- hl_cb_put(cb);
+ hl_cb_put(cq_cb);
+ts_registration_exit:
+ hl_ctx_put(ctx);
+
+ return rc;
+
+put_ts_buff:
+ hl_ts_put(ts_buff);
+put_cq_cb:
+ hl_cb_put(cq_cb);
+put_ctx:
hl_ctx_put(ctx);
return rc;
@@ -3049,6 +3191,12 @@ wait_again:
interrupt->interrupt_id);
rc = -EINTR;
} else {
+ /* The wait has timed-out. We don't know anything beyond that
+ * because the workload wasn't submitted through the driver.
+ * Therefore, from driver's perspective, the workload is still
+ * executing.
+ */
+ rc = 0;
*status = HL_WAIT_CS_STATUS_BUSY;
}
@@ -3101,23 +3249,20 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data)
interrupt = &hdev->user_interrupt[interrupt_id - first_interrupt];
if (args->in.flags & HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ)
- rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->cb_mgr,
+ rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->cb_mgr, &hpriv->ts_mem_mgr,
args->in.interrupt_timeout_us, args->in.cq_counters_handle,
args->in.cq_counters_offset,
- args->in.target, interrupt, &status,
- &timestamp);
+ args->in.target, interrupt,
+ !!(args->in.flags & HL_WAIT_CS_FLAGS_REGISTER_INTERRUPT),
+ args->in.timestamp_handle, args->in.timestamp_offset,
+ &status, &timestamp);
else
rc = _hl_interrupt_wait_ioctl_user_addr(hdev, hpriv->ctx,
args->in.interrupt_timeout_us, args->in.addr,
args->in.target, interrupt, &status,
&timestamp);
- if (rc) {
- if (rc != -EINTR)
- dev_err_ratelimited(hdev->dev,
- "interrupt_wait_ioctl failed (%d)\n", rc);
-
+ if (rc)
return rc;
- }
memset(args, 0, sizeof(*args));
args->out.status = status;
diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c
index fc084ee5106e..f18495545854 100644
--- a/drivers/misc/habanalabs/common/debugfs.c
+++ b/drivers/misc/habanalabs/common/debugfs.c
@@ -890,6 +890,8 @@ static ssize_t hl_set_power_state(struct file *f, const char __user *buf,
pci_set_power_state(hdev->pdev, PCI_D0);
pci_restore_state(hdev->pdev);
rc = pci_enable_device(hdev->pdev);
+ if (rc < 0)
+ return rc;
} else if (value == 2) {
pci_save_state(hdev->pdev);
pci_disable_device(hdev->pdev);
@@ -1054,42 +1056,12 @@ static ssize_t hl_device_write(struct file *f, const char __user *buf,
static ssize_t hl_clk_gate_read(struct file *f, char __user *buf,
size_t count, loff_t *ppos)
{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- char tmp_buf[200];
- ssize_t rc;
-
- if (*ppos)
- return 0;
-
- sprintf(tmp_buf, "0x%llx\n", hdev->clock_gating_mask);
- rc = simple_read_from_buffer(buf, count, ppos, tmp_buf,
- strlen(tmp_buf) + 1);
-
- return rc;
+ return 0;
}
static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 value;
- ssize_t rc;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev,
- "Can't change clock gating during reset\n");
- return 0;
- }
-
- rc = kstrtoull_from_user(buf, count, 16, &value);
- if (rc)
- return rc;
-
- hdev->clock_gating_mask = value;
- hdev->asic_funcs->set_clock_gating(hdev);
-
return count;
}
@@ -1101,6 +1073,9 @@ static ssize_t hl_stop_on_err_read(struct file *f, char __user *buf,
char tmp_buf[200];
ssize_t rc;
+ if (!hdev->asic_prop.configurable_stop_on_err)
+ return -EOPNOTSUPP;
+
if (*ppos)
return 0;
@@ -1119,6 +1094,9 @@ static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf,
u32 value;
ssize_t rc;
+ if (!hdev->asic_prop.configurable_stop_on_err)
+ return -EOPNOTSUPP;
+
if (hdev->reset_info.in_reset) {
dev_warn_ratelimited(hdev->dev,
"Can't change stop on error during reset\n");
diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c
index 733338ab6f1d..dc9341a64541 100644
--- a/drivers/misc/habanalabs/common/device.c
+++ b/drivers/misc/habanalabs/common/device.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -13,6 +13,8 @@
#include <linux/pci.h>
#include <linux/hwmon.h>
+#define HL_RESET_DELAY_USEC 10000 /* 10ms */
+
enum hl_device_status hl_device_status(struct hl_device *hdev)
{
enum hl_device_status status;
@@ -145,6 +147,7 @@ static int hl_device_release(struct inode *inode, struct file *filp)
hl_release_pending_user_interrupts(hpriv->hdev);
hl_cb_mgr_fini(hdev, &hpriv->cb_mgr);
+ hl_ts_mgr_fini(hpriv->hdev, &hpriv->ts_mem_mgr);
hl_ctx_mgr_fini(hdev, &hpriv->ctx_mgr);
if (!hl_hpriv_put(hpriv))
@@ -209,6 +212,9 @@ static int hl_mmap(struct file *filp, struct vm_area_struct *vma)
case HL_MMAP_TYPE_BLOCK:
return hl_hw_block_mmap(hpriv, vma);
+
+ case HL_MMAP_TYPE_TS_BUFF:
+ return hl_ts_mmap(hpriv, vma);
}
return -EINVAL;
@@ -410,10 +416,10 @@ static int device_early_init(struct hl_device *hdev)
goto free_cq_wq;
}
- hdev->sob_reset_wq = alloc_workqueue("hl-sob-reset", WQ_UNBOUND, 0);
- if (!hdev->sob_reset_wq) {
+ hdev->ts_free_obj_wq = alloc_workqueue("hl-ts-free-obj", WQ_UNBOUND, 0);
+ if (!hdev->ts_free_obj_wq) {
dev_err(hdev->dev,
- "Failed to allocate SOB reset workqueue\n");
+ "Failed to allocate Timestamp registration free workqueue\n");
rc = -ENOMEM;
goto free_eq_wq;
}
@@ -422,7 +428,7 @@ static int device_early_init(struct hl_device *hdev)
GFP_KERNEL);
if (!hdev->hl_chip_info) {
rc = -ENOMEM;
- goto free_sob_reset_wq;
+ goto free_ts_free_wq;
}
rc = hl_mmu_if_set_funcs(hdev);
@@ -461,8 +467,8 @@ free_cb_mgr:
hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr);
free_chip_info:
kfree(hdev->hl_chip_info);
-free_sob_reset_wq:
- destroy_workqueue(hdev->sob_reset_wq);
+free_ts_free_wq:
+ destroy_workqueue(hdev->ts_free_obj_wq);
free_eq_wq:
destroy_workqueue(hdev->eq_wq);
free_cq_wq:
@@ -501,7 +507,7 @@ static void device_early_fini(struct hl_device *hdev)
kfree(hdev->hl_chip_info);
- destroy_workqueue(hdev->sob_reset_wq);
+ destroy_workqueue(hdev->ts_free_obj_wq);
destroy_workqueue(hdev->eq_wq);
destroy_workqueue(hdev->device_reset_work.wq);
@@ -610,7 +616,7 @@ int hl_device_utilization(struct hl_device *hdev, u32 *utilization)
u64 max_power, curr_power, dc_power, dividend;
int rc;
- max_power = hdev->asic_prop.max_power_default;
+ max_power = hdev->max_power;
dc_power = hdev->asic_prop.dc_power_default;
rc = hl_fw_cpucp_power_get(hdev, &curr_power);
@@ -644,9 +650,6 @@ int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool en
hdev->in_debug = 0;
- if (!hdev->reset_info.hard_reset_pending)
- hdev->asic_funcs->set_clock_gating(hdev);
-
goto out;
}
@@ -657,7 +660,6 @@ int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool en
goto out;
}
- hdev->asic_funcs->disable_clock_gating(hdev);
hdev->in_debug = 1;
out:
@@ -685,7 +687,8 @@ static void take_release_locks(struct hl_device *hdev)
mutex_unlock(&hdev->fpriv_ctrl_list_lock);
}
-static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset)
+static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset,
+ bool skip_wq_flush)
{
if (hard_reset)
device_late_fini(hdev);
@@ -698,7 +701,7 @@ static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_r
hdev->asic_funcs->halt_engines(hdev, hard_reset, fw_reset);
/* Go over all the queues, release all CS and their jobs */
- hl_cs_rollback_all(hdev);
+ hl_cs_rollback_all(hdev, skip_wq_flush);
/* Release all pending user interrupts, each pending user interrupt
* holds a reference to user context
@@ -978,7 +981,8 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags)
int hl_device_reset(struct hl_device *hdev, u32 flags)
{
bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false,
- reset_upon_device_release = false, schedule_hard_reset = false;
+ reset_upon_device_release = false, schedule_hard_reset = false,
+ skip_wq_flush, delay_reset;
u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0};
struct hl_ctx *ctx;
int i, rc;
@@ -991,6 +995,8 @@ int hl_device_reset(struct hl_device *hdev, u32 flags)
hard_reset = !!(flags & HL_DRV_RESET_HARD);
from_hard_reset_thread = !!(flags & HL_DRV_RESET_FROM_RESET_THR);
fw_reset = !!(flags & HL_DRV_RESET_BYPASS_REQ_TO_FW);
+ skip_wq_flush = !!(flags & HL_DRV_RESET_DEV_RELEASE);
+ delay_reset = !!(flags & HL_DRV_RESET_DELAY);
if (!hard_reset && !hdev->asic_prop.supports_soft_reset) {
hard_instead_soft = true;
@@ -1040,6 +1046,9 @@ do_reset:
hdev->reset_info.in_reset = 1;
spin_unlock(&hdev->reset_info.lock);
+ if (delay_reset)
+ usleep_range(HL_RESET_DELAY_USEC, HL_RESET_DELAY_USEC << 1);
+
handle_reset_trigger(hdev, flags);
/* This still allows the completion of some KDMA ops */
@@ -1076,7 +1085,7 @@ again:
return 0;
}
- cleanup_resources(hdev, hard_reset, fw_reset);
+ cleanup_resources(hdev, hard_reset, fw_reset, skip_wq_flush);
kill_processes:
if (hard_reset) {
@@ -1232,7 +1241,7 @@ kill_processes:
goto out_err;
}
- hl_set_max_power(hdev);
+ hl_fw_set_max_power(hdev);
} else {
rc = hdev->asic_funcs->non_hard_reset_late_init(hdev);
if (rc) {
@@ -1297,11 +1306,14 @@ out_err:
hdev->reset_info.hard_reset_cnt++;
} else if (reset_upon_device_release) {
dev_err(hdev->dev, "Failed to reset device after user release\n");
+ flags |= HL_DRV_RESET_HARD;
+ flags &= ~HL_DRV_RESET_DEV_RELEASE;
hard_reset = true;
goto again;
} else {
dev_err(hdev->dev, "Failed to do soft-reset\n");
hdev->reset_info.soft_reset_cnt++;
+ flags |= HL_DRV_RESET_HARD;
hard_reset = true;
goto again;
}
@@ -1538,7 +1550,8 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
/* Need to call this again because the max power might change,
* depending on card type for certain ASICs
*/
- hl_set_max_power(hdev);
+ if (hdev->asic_prop.set_max_power_on_device_init)
+ hl_fw_set_max_power(hdev);
/*
* hl_hwmon_init() must be called after device_late_init(), because only
@@ -1682,7 +1695,7 @@ void hl_device_fini(struct hl_device *hdev)
hl_hwmon_fini(hdev);
- cleanup_resources(hdev, true, false);
+ cleanup_resources(hdev, true, false, false);
/* Kill processes here after CS rollback. This is because the process
* can't really exit until all its CSs are done, which is what we
diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c
index 6775c5c3166b..3262126cc7ca 100644
--- a/drivers/misc/habanalabs/common/firmware_if.c
+++ b/drivers/misc/habanalabs/common/firmware_if.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -214,7 +214,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
dma_addr_t pkt_dma_addr;
struct hl_bd *sent_bd;
u32 tmp, expected_ack_val, pi;
- int rc = 0;
+ int rc;
pkt = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, len,
&pkt_dma_addr);
@@ -228,8 +228,11 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
mutex_lock(&hdev->send_cpu_message_lock);
- if (hdev->disabled)
+ /* CPU-CP messages can be sent during soft-reset */
+ if (hdev->disabled && !hdev->reset_info.is_in_soft_reset) {
+ rc = 0;
goto out;
+ }
if (hdev->device_cpu_disabled) {
rc = -EIO;
@@ -958,15 +961,17 @@ int hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index,
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc)
+ if (rc) {
dev_err(hdev->dev, "Failed to read PLL info, error %d\n", rc);
+ return rc;
+ }
pll_freq_arr[0] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT0_MASK, result);
pll_freq_arr[1] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT1_MASK, result);
pll_freq_arr[2] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT2_MASK, result);
pll_freq_arr[3] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT3_MASK, result);
- return rc;
+ return 0;
}
int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power)
@@ -1202,8 +1207,6 @@ static int hl_fw_read_preboot_caps(struct hl_device *hdev,
hdev,
cpu_boot_status_reg,
status,
- (status == CPU_BOOT_STATUS_IN_UBOOT) ||
- (status == CPU_BOOT_STATUS_DRAM_RDY) ||
(status == CPU_BOOT_STATUS_NIC_FW_RDY) ||
(status == CPU_BOOT_STATUS_READY_TO_BOOT) ||
(status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT),
@@ -2682,3 +2685,138 @@ int hl_fw_init_cpu(struct hl_device *hdev)
hl_fw_dynamic_init_cpu(hdev, fw_loader) :
hl_fw_static_init_cpu(hdev, fw_loader);
}
+
+void hl_fw_set_pll_profile(struct hl_device *hdev)
+{
+ hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index,
+ hdev->asic_prop.max_freq_value);
+}
+
+int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk)
+{
+ long value;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ if (!hdev->pdev) {
+ *cur_clk = 0;
+ *max_clk = 0;
+ return 0;
+ }
+
+ value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
+
+ if (value < 0) {
+ dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", value);
+ return value;
+ }
+
+ *max_clk = (value / 1000 / 1000);
+
+ value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
+
+ if (value < 0) {
+ dev_err(hdev->dev, "Failed to retrieve device current clock %ld\n", value);
+ return value;
+ }
+
+ *cur_clk = (value / 1000 / 1000);
+
+ return 0;
+}
+
+long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr)
+{
+ struct cpucp_packet pkt;
+ u32 used_pll_idx;
+ u64 result;
+ int rc;
+
+ rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
+ if (rc)
+ return rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ if (curr)
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_CURR_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ else
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_GET << CPUCP_PKT_CTL_OPCODE_SHIFT);
+
+ pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
+
+ if (rc) {
+ dev_err(hdev->dev, "Failed to get frequency of PLL %d, error %d\n",
+ used_pll_idx, rc);
+ return rc;
+ }
+
+ return (long) result;
+}
+
+void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq)
+{
+ struct cpucp_packet pkt;
+ u32 used_pll_idx;
+ int rc;
+
+ rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
+ if (rc)
+ return;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
+ pkt.value = cpu_to_le64(freq);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+
+ if (rc)
+ dev_err(hdev->dev, "Failed to set frequency to PLL %d, error %d\n",
+ used_pll_idx, rc);
+}
+
+long hl_fw_get_max_power(struct hl_device *hdev)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
+
+ if (rc) {
+ dev_err(hdev->dev, "Failed to get max power, error %d\n", rc);
+ return rc;
+ }
+
+ return result;
+}
+
+void hl_fw_set_max_power(struct hl_device *hdev)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ /* TODO: remove this after simulator supports this packet */
+ if (!hdev->pdev)
+ return;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.value = cpu_to_le64(hdev->max_power);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+
+ if (rc)
+ dev_err(hdev->dev, "Failed to set max power, error %d\n", rc);
+}
diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h
index cb710fd478b6..1edaf6ab67bd 100644
--- a/drivers/misc/habanalabs/common/habanalabs.h
+++ b/drivers/misc/habanalabs/common/habanalabs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
*
- * Copyright 2016-2021 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
@@ -31,14 +31,15 @@
#define HL_NAME "habanalabs"
/* Use upper bits of mmap offset to store habana driver specific information.
- * bits[63:61] - Encode mmap type
+ * bits[63:59] - Encode mmap type
* bits[45:0] - mmap offset value
*
* NOTE: struct vm_area_struct.vm_pgoff uses offset in pages. Hence, these
* defines are w.r.t to PAGE_SIZE
*/
-#define HL_MMAP_TYPE_SHIFT (61 - PAGE_SHIFT)
-#define HL_MMAP_TYPE_MASK (0x7ull << HL_MMAP_TYPE_SHIFT)
+#define HL_MMAP_TYPE_SHIFT (59 - PAGE_SHIFT)
+#define HL_MMAP_TYPE_MASK (0x1full << HL_MMAP_TYPE_SHIFT)
+#define HL_MMAP_TYPE_TS_BUFF (0x10ull << HL_MMAP_TYPE_SHIFT)
#define HL_MMAP_TYPE_BLOCK (0x4ull << HL_MMAP_TYPE_SHIFT)
#define HL_MMAP_TYPE_CB (0x2ull << HL_MMAP_TYPE_SHIFT)
@@ -141,6 +142,9 @@ enum hl_mmu_page_table_location {
*
* - HL_DRV_RESET_FW_FATAL_ERR
* Set if reset is due to a fatal error from FW
+ *
+ * - HL_DRV_RESET_DELAY
+ * Set if a delay should be added before the reset
*/
#define HL_DRV_RESET_HARD (1 << 0)
@@ -150,6 +154,7 @@ enum hl_mmu_page_table_location {
#define HL_DRV_RESET_DEV_RELEASE (1 << 4)
#define HL_DRV_RESET_BYPASS_REQ_TO_FW (1 << 5)
#define HL_DRV_RESET_FW_FATAL_ERR (1 << 6)
+#define HL_DRV_RESET_DELAY (1 << 7)
#define HL_MAX_SOBS_PER_MONITOR 8
@@ -402,8 +407,11 @@ enum hl_device_hw_state {
* @hop4_mask: mask to get the PTE address in hop 4.
* @hop5_mask: mask to get the PTE address in hop 5.
* @last_mask: mask to get the bit indicating this is the last hop.
+ * @pgt_size: size for page tables.
* @page_size: default page size used to allocate memory.
* @num_hops: The amount of hops supported by the translation table.
+ * @hop_table_size: HOP table size.
+ * @hop0_tables_total_size: total size for all HOP0 tables.
* @host_resident: Should the MMU page table reside in host memory or in the
* device DRAM.
*/
@@ -423,8 +431,11 @@ struct hl_mmu_properties {
u64 hop4_mask;
u64 hop5_mask;
u64 last_mask;
+ u64 pgt_size;
u32 page_size;
u32 num_hops;
+ u32 hop_table_size;
+ u32 hop0_tables_total_size;
u8 host_resident;
};
@@ -554,6 +565,9 @@ struct hl_hints_range {
* use-case of doing soft-reset in training (due
* to the fact that training runs on multiple
* devices)
+ * @configurable_stop_on_err: is stop-on-error option configurable via debugfs.
+ * @set_max_power_on_device_init: true if need to set max power in F/W on device init.
+ * @supports_user_set_page_size: true if user can set the allocation page size.
*/
struct asic_fixed_properties {
struct hw_queue_properties *hw_queues_props;
@@ -637,6 +651,9 @@ struct asic_fixed_properties {
u8 use_get_power_for_reset_history;
u8 supports_soft_reset;
u8 allow_inference_soft_reset;
+ u8 configurable_stop_on_err;
+ u8 set_max_power_on_device_init;
+ u8 supports_user_set_page_size;
};
/**
@@ -704,6 +721,40 @@ struct hl_cb_mgr {
};
/**
+ * struct hl_ts_mgr - describes the timestamp registration memory manager.
+ * @ts_lock: protects ts_handles.
+ * @ts_handles: an idr to hold all ts bufferes handles.
+ */
+struct hl_ts_mgr {
+ spinlock_t ts_lock;
+ struct idr ts_handles;
+};
+
+/**
+ * struct hl_ts_buff - describes a timestamp buffer.
+ * @refcount: reference counter for usage of the buffer.
+ * @hdev: pointer to device this buffer belongs to.
+ * @mmap: true if the buff is currently mapped to user.
+ * @kernel_buff_address: Holds the internal buffer's kernel virtual address.
+ * @user_buff_address: Holds the user buffer's kernel virtual address.
+ * @id: the buffer ID.
+ * @mmap_size: Holds the buffer size that was mmaped.
+ * @kernel_buff_size: Holds the internal kernel buffer size.
+ * @user_buff_size: Holds the user buffer size.
+ */
+struct hl_ts_buff {
+ struct kref refcount;
+ struct hl_device *hdev;
+ atomic_t mmap;
+ void *kernel_buff_address;
+ void *user_buff_address;
+ u32 id;
+ u32 mmap_size;
+ u32 kernel_buff_size;
+ u32 user_buff_size;
+};
+
+/**
* struct hl_cb - describes a Command Buffer.
* @refcount: reference counter for usage of the CB.
* @hdev: pointer to device this CB belongs to.
@@ -881,8 +932,53 @@ struct hl_user_interrupt {
};
/**
+ * struct timestamp_reg_free_node - holds the timestamp registration free objects node
+ * @free_objects_node: node in the list free_obj_jobs
+ * @cq_cb: pointer to cq command buffer to be freed
+ * @ts_buff: pointer to timestamp buffer to be freed
+ */
+struct timestamp_reg_free_node {
+ struct list_head free_objects_node;
+ struct hl_cb *cq_cb;
+ struct hl_ts_buff *ts_buff;
+};
+
+/* struct timestamp_reg_work_obj - holds the timestamp registration free objects job
+ * the job will be to pass over the free_obj_jobs list and put refcount to objects
+ * in each node of the list
+ * @free_obj: workqueue object to free timestamp registration node objects
+ * @hdev: pointer to the device structure
+ * @free_obj_head: list of free jobs nodes (node type timestamp_reg_free_node)
+ */
+struct timestamp_reg_work_obj {
+ struct work_struct free_obj;
+ struct hl_device *hdev;
+ struct list_head *free_obj_head;
+};
+
+/* struct timestamp_reg_info - holds the timestamp registration related data.
+ * @ts_buff: pointer to the timestamp buffer which include both user/kernel buffers.
+ * relevant only when doing timestamps records registration.
+ * @cq_cb: pointer to CQ counter CB.
+ * @timestamp_kernel_addr: timestamp handle address, where to set timestamp
+ * relevant only when doing timestamps records
+ * registration.
+ * @in_use: indicates if the node already in use. relevant only when doing
+ * timestamps records registration, since in this case the driver
+ * will have it's own buffer which serve as a records pool instead of
+ * allocating records dynamically.
+ */
+struct timestamp_reg_info {
+ struct hl_ts_buff *ts_buff;
+ struct hl_cb *cq_cb;
+ u64 *timestamp_kernel_addr;
+ u8 in_use;
+};
+
+/**
* struct hl_user_pending_interrupt - holds a context to a user thread
* pending on an interrupt
+ * @ts_reg_info: holds the timestamps registration nodes info
* @wait_list_node: node in the list of user threads pending on an interrupt
* @fence: hl fence object for interrupt completion
* @cq_target_value: CQ target value
@@ -890,10 +986,11 @@ struct hl_user_interrupt {
* handler for taget value comparison
*/
struct hl_user_pending_interrupt {
- struct list_head wait_list_node;
- struct hl_fence fence;
- u64 cq_target_value;
- u64 *cq_kernel_addr;
+ struct timestamp_reg_info ts_reg_info;
+ struct list_head wait_list_node;
+ struct hl_fence fence;
+ u64 cq_target_value;
+ u64 *cq_kernel_addr;
};
/**
@@ -1155,7 +1252,6 @@ struct fw_load_mgr {
* internal memory via DMA engine.
* @add_device_attr: add ASIC specific device attributes.
* @handle_eqe: handle event queue entry (IRQ) from CPU-CP.
- * @set_pll_profile: change PLL profile (manual/automatic).
* @get_events_stat: retrieve event queue entries histogram.
* @read_pte: read MMU page table entry from DRAM.
* @write_pte: write MMU page table entry to DRAM.
@@ -1164,9 +1260,6 @@ struct fw_load_mgr {
* @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with
* ASID-VA-size mask.
* @send_heartbeat: send is-alive packet to CPU-CP and verify response.
- * @set_clock_gating: enable/disable clock gating per engine according to
- * clock gating mask in hdev
- * @disable_clock_gating: disable clock gating completely
* @debug_coresight: perform certain actions on Coresight for debugging.
* @is_device_idle: return true if device is idle, false otherwise.
* @non_hard_reset_late_init: perform certain actions needed after a reset which is not hard-reset
@@ -1187,7 +1280,6 @@ struct fw_load_mgr {
* @halt_coresight: stop the ETF and ETR traces.
* @ctx_init: context dependent initialization.
* @ctx_fini: context dependent cleanup.
- * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz
* @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index.
* @load_firmware_to_device: load the firmware to the device's memory
* @load_boot_fit_to_device: load boot fit to device's memory
@@ -1225,6 +1317,8 @@ struct fw_load_mgr {
* @get_sob_addr: get SOB base address offset.
* @set_pci_memory_regions: setting properties of PCI memory regions
* @get_stream_master_qid_arr: get pointer to stream masters QID array
+ * @is_valid_dram_page_size: return true if page size is supported in device
+ * memory allocation, otherwise false.
*/
struct hl_asic_funcs {
int (*early_init)(struct hl_device *hdev);
@@ -1285,12 +1379,10 @@ struct hl_asic_funcs {
bool user_address, u64 val);
int (*debugfs_read_dma)(struct hl_device *hdev, u64 addr, u32 size,
void *blob_addr);
- void (*add_device_attr)(struct hl_device *hdev,
- struct attribute_group *dev_attr_grp);
+ void (*add_device_attr)(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp,
+ struct attribute_group *dev_vrm_attr_grp);
void (*handle_eqe)(struct hl_device *hdev,
struct hl_eq_entry *eq_entry);
- void (*set_pll_profile)(struct hl_device *hdev,
- enum hl_pll_frequency freq);
void* (*get_events_stat)(struct hl_device *hdev, bool aggregate,
u32 *size);
u64 (*read_pte)(struct hl_device *hdev, u64 addr);
@@ -1300,8 +1392,6 @@ struct hl_asic_funcs {
int (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard,
u32 flags, u32 asid, u64 va, u64 size);
int (*send_heartbeat)(struct hl_device *hdev);
- void (*set_clock_gating)(struct hl_device *hdev);
- void (*disable_clock_gating)(struct hl_device *hdev);
int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data);
bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr,
u8 mask_len, struct seq_file *s);
@@ -1320,7 +1410,6 @@ struct hl_asic_funcs {
void (*halt_coresight)(struct hl_device *hdev, struct hl_ctx *ctx);
int (*ctx_init)(struct hl_ctx *ctx);
void (*ctx_fini)(struct hl_ctx *ctx);
- int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk);
u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx);
int (*load_firmware_to_device)(struct hl_device *hdev);
int (*load_boot_fit_to_device)(struct hl_device *hdev);
@@ -1355,6 +1444,7 @@ struct hl_asic_funcs {
u32 (*get_sob_addr)(struct hl_device *hdev, u32 sob_id);
void (*set_pci_memory_regions)(struct hl_device *hdev);
u32* (*get_stream_master_qid_arr)(void);
+ bool (*is_valid_dram_page_size)(u32 page_size);
};
@@ -1742,6 +1832,8 @@ struct hl_vm_hw_block_list_node {
* @pages: the physical page array.
* @npages: num physical pages in the pack.
* @total_size: total size of all the pages in this list.
+ * @node: used to attach to deletion list that is used when all the allocations are cleared
+ * at the teardown of the context.
* @mapping_cnt: number of shared mappings.
* @exporting_cnt: number of dma-buf exporting.
* @asid: the context related to this list.
@@ -1757,6 +1849,7 @@ struct hl_vm_phys_pg_pack {
u64 *pages;
u64 npages;
u64 total_size;
+ struct list_head node;
atomic_t mapping_cnt;
u32 exporting_cnt;
u32 asid;
@@ -1834,6 +1927,7 @@ struct hl_debug_params {
* @ctx: current executing context. TODO: remove for multiple ctx per process
* @ctx_mgr: context manager to handle multiple context for this FD.
* @cb_mgr: command buffer manager to handle multiple buffers for this FD.
+ * @ts_mem_mgr: timestamp registration manager for alloc/free/map timestamp buffers.
* @debugfs_list: list of relevant ASIC debugfs.
* @dev_node: node in the device list of file private data
* @refcount: number of related contexts.
@@ -1846,6 +1940,7 @@ struct hl_fpriv {
struct hl_ctx *ctx;
struct hl_ctx_mgr ctx_mgr;
struct hl_cb_mgr cb_mgr;
+ struct hl_ts_mgr ts_mem_mgr;
struct list_head debugfs_list;
struct list_head dev_node;
struct kref refcount;
@@ -2518,7 +2613,7 @@ struct hl_reset_info {
* @cq_wq: work queues of completion queues for executing work in process
* context.
* @eq_wq: work queue of event queue for executing work in process context.
- * @sob_reset_wq: work queue for sob reset executions.
+ * @ts_free_obj_wq: work queue for timestamp registration objects release.
* @kernel_ctx: Kernel driver context structure.
* @kernel_queues: array of hl_hw_queue.
* @cs_mirror_list: CS mirror list for TDR.
@@ -2569,9 +2664,6 @@ struct hl_reset_info {
* @max_power: the max power of the device, as configured by the sysadmin. This
* value is saved so in case of hard-reset, the driver will restore
* this value and update the F/W after the re-initialization
- * @clock_gating_mask: is clock gating enabled. bitmask that represents the
- * different engines. See debugfs-driver-habanalabs for
- * details.
* @boot_error_status_mask: contains a mask of the device boot error status.
* Each bit represents a different error, according to
* the defines in hl_boot_if.h. If the bit is cleared,
@@ -2611,8 +2703,6 @@ struct hl_reset_info {
* @in_debug: whether the device is in a state where the profiling/tracing infrastructure
* can be used. This indication is needed because in some ASICs we need to do
* specific operations to enable that infrastructure.
- * @power9_64bit_dma_enable: true to enable 64-bit DMA mask support. Relevant
- * only to POWER9 machines.
* @cdev_sysfs_created: were char devices and sysfs nodes created.
* @stop_on_err: true if engines should stop on error.
* @supports_sync_stream: is sync stream supported.
@@ -2651,7 +2741,7 @@ struct hl_device {
struct hl_user_interrupt common_user_interrupt;
struct workqueue_struct **cq_wq;
struct workqueue_struct *eq_wq;
- struct workqueue_struct *sob_reset_wq;
+ struct workqueue_struct *ts_free_obj_wq;
struct hl_ctx *kernel_ctx;
struct hl_hw_queue *kernel_queues;
struct list_head cs_mirror_list;
@@ -2710,7 +2800,6 @@ struct hl_device {
atomic64_t dram_used_mem;
u64 timeout_jiffies;
u64 max_power;
- u64 clock_gating_mask;
u64 boot_error_status_mask;
u64 dram_pci_bar_start;
u64 last_successful_open_jif;
@@ -2736,7 +2825,6 @@ struct hl_device {
u8 device_cpu_disabled;
u8 dma_mask;
u8 in_debug;
- u8 power9_64bit_dma_enable;
u8 cdev_sysfs_created;
u8 stop_on_err;
u8 supports_sync_stream;
@@ -2970,7 +3058,7 @@ int hl_cb_pool_fini(struct hl_device *hdev);
int hl_cb_va_pool_init(struct hl_ctx *ctx);
void hl_cb_va_pool_fini(struct hl_ctx *ctx);
-void hl_cs_rollback_all(struct hl_device *hdev);
+void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush);
struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev,
enum hl_queue_type queue_type, bool is_kernel_allocated_cb);
void hl_sob_reset_error(struct kref *ref);
@@ -3024,6 +3112,9 @@ int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size);
int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags);
int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,
u32 flags, u32 asid, u64 va, u64 size);
+u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte);
+u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,
+ u8 hop_idx, u64 hop_addr, u64 virt_addr);
void hl_mmu_swap_out(struct hl_ctx *ctx);
void hl_mmu_swap_in(struct hl_ctx *ctx);
int hl_mmu_if_set_funcs(struct hl_device *hdev);
@@ -3094,39 +3185,26 @@ enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr);
int hl_pci_init(struct hl_device *hdev);
void hl_pci_fini(struct hl_device *hdev);
-long hl_get_frequency(struct hl_device *hdev, u32 pll_index,
- bool curr);
-void hl_set_frequency(struct hl_device *hdev, u32 pll_index,
- u64 freq);
-int hl_get_temperature(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value);
-int hl_set_temperature(struct hl_device *hdev,
- int sensor_index, u32 attr, long value);
-int hl_get_voltage(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value);
-int hl_get_current(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value);
-int hl_get_fan_speed(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value);
-int hl_get_pwm_info(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value);
-void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr,
- long value);
-u64 hl_get_max_power(struct hl_device *hdev);
-void hl_set_max_power(struct hl_device *hdev);
-int hl_set_voltage(struct hl_device *hdev,
- int sensor_index, u32 attr, long value);
-int hl_set_current(struct hl_device *hdev,
- int sensor_index, u32 attr, long value);
-int hl_set_power(struct hl_device *hdev,
- int sensor_index, u32 attr, long value);
-int hl_get_power(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value);
-int hl_get_clk_rate(struct hl_device *hdev,
- u32 *cur_clk, u32 *max_clk);
-void hl_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq);
-void hl_add_device_attr(struct hl_device *hdev,
- struct attribute_group *dev_attr_grp);
+long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr);
+void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq);
+int hl_get_temperature(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
+int hl_set_temperature(struct hl_device *hdev, int sensor_index, u32 attr, long value);
+int hl_get_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
+int hl_get_current(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
+int hl_get_fan_speed(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
+int hl_get_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
+void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long value);
+long hl_fw_get_max_power(struct hl_device *hdev);
+void hl_fw_set_max_power(struct hl_device *hdev);
+int hl_set_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long value);
+int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value);
+int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value);
+int hl_get_power(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
+int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk);
+void hl_fw_set_pll_profile(struct hl_device *hdev);
+void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp);
+void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp);
+
void hw_sob_get(struct hl_hw_sob *hw_sob);
void hw_sob_put(struct hl_hw_sob *hw_sob);
void hl_encaps_handle_do_release(struct kref *ref);
@@ -3146,6 +3224,11 @@ __printf(4, 5) int hl_snprintf_resize(char **buf, size_t *size, size_t *offset,
const char *format, ...);
char *hl_format_as_binary(char *buf, size_t buf_len, u32 n);
const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type);
+void hl_ts_mgr_init(struct hl_ts_mgr *mgr);
+void hl_ts_mgr_fini(struct hl_device *hdev, struct hl_ts_mgr *mgr);
+int hl_ts_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma);
+struct hl_ts_buff *hl_ts_get(struct hl_device *hdev, struct hl_ts_mgr *mgr, u32 handle);
+void hl_ts_put(struct hl_ts_buff *buff);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c
index 690b763c7a95..ca404ed9d9a7 100644
--- a/drivers/misc/habanalabs/common/habanalabs_drv.c
+++ b/drivers/misc/habanalabs/common/habanalabs_drv.c
@@ -140,6 +140,7 @@ int hl_device_open(struct inode *inode, struct file *filp)
hl_cb_mgr_init(&hpriv->cb_mgr);
hl_ctx_mgr_init(&hpriv->ctx_mgr);
+ hl_ts_mgr_init(&hpriv->ts_mem_mgr);
hpriv->taskpid = get_task_pid(current, PIDTYPE_PID);
@@ -184,6 +185,7 @@ int hl_device_open(struct inode *inode, struct file *filp)
out_err:
mutex_unlock(&hdev->fpriv_list_lock);
hl_cb_mgr_fini(hpriv->hdev, &hpriv->cb_mgr);
+ hl_ts_mgr_fini(hpriv->hdev, &hpriv->ts_mem_mgr);
hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr);
filp->private_data = NULL;
mutex_destroy(&hpriv->restore_phase_mutex);
@@ -256,7 +258,6 @@ static void set_driver_behavior_per_device(struct hl_device *hdev)
hdev->cpu_queues_enable = 1;
hdev->heartbeat = 1;
hdev->mmu_enable = 1;
- hdev->clock_gating_mask = ULONG_MAX;
hdev->sram_scrambler_enable = 1;
hdev->dram_scrambler_enable = 1;
hdev->bmc_enable = 1;
diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c
index 3ba3a8ffda3e..c13a3c2a7013 100644
--- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c
+++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -92,8 +92,8 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args)
hw_ip.psoc_pci_pll_od = prop->psoc_pci_pll_od;
hw_ip.psoc_pci_pll_div_factor = prop->psoc_pci_pll_div_factor;
- hw_ip.first_available_interrupt_id =
- prop->first_available_user_msix_interrupt;
+ hw_ip.first_available_interrupt_id = prop->first_available_user_msix_interrupt;
+ hw_ip.number_of_user_interrupts = prop->user_interrupt_count;
hw_ip.server_type = prop->server_type;
return copy_to_user(out, &hw_ip,
@@ -251,13 +251,12 @@ static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args)
if ((!max_size) || (!out))
return -EINVAL;
- rc = hdev->asic_funcs->get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz,
- &clk_rate.max_clk_rate_mhz);
+ rc = hl_fw_get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, &clk_rate.max_clk_rate_mhz);
if (rc)
return rc;
- return copy_to_user(out, &clk_rate,
- min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0;
+ return copy_to_user(out, &clk_rate, min_t(size_t, max_size, sizeof(clk_rate)))
+ ? -EFAULT : 0;
}
static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args)
diff --git a/drivers/misc/habanalabs/common/hwmgr.c b/drivers/misc/habanalabs/common/hwmgr.c
deleted file mode 100644
index 5451019f143f..000000000000
--- a/drivers/misc/habanalabs/common/hwmgr.c
+++ /dev/null
@@ -1,117 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2019-2021 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-void hl_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq)
-{
- hl_set_frequency(hdev, hdev->asic_prop.clk_pll_index,
- hdev->asic_prop.max_freq_value);
-}
-
-int hl_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk)
-{
- long value;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
-
- if (value < 0) {
- dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n",
- value);
- return value;
- }
-
- *max_clk = (value / 1000 / 1000);
-
- value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
-
- if (value < 0) {
- dev_err(hdev->dev,
- "Failed to retrieve device current clock %ld\n",
- value);
- return value;
- }
-
- *cur_clk = (value / 1000 / 1000);
-
- return 0;
-}
-
-static ssize_t clk_max_freq_mhz_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long value;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
-
- hdev->asic_prop.max_freq_value = value;
-
- return sprintf(buf, "%lu\n", (value / 1000 / 1000));
-}
-
-static ssize_t clk_max_freq_mhz_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- int rc;
- u64 value;
-
- if (!hl_device_operational(hdev, NULL)) {
- count = -ENODEV;
- goto fail;
- }
-
- rc = kstrtoull(buf, 0, &value);
- if (rc) {
- count = -EINVAL;
- goto fail;
- }
-
- hdev->asic_prop.max_freq_value = value * 1000 * 1000;
-
- hl_set_frequency(hdev, hdev->asic_prop.clk_pll_index,
- hdev->asic_prop.max_freq_value);
-
-fail:
- return count;
-}
-
-static ssize_t clk_cur_freq_mhz_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long value;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
-
- return sprintf(buf, "%lu\n", (value / 1000 / 1000));
-}
-
-static DEVICE_ATTR_RW(clk_max_freq_mhz);
-static DEVICE_ATTR_RO(clk_cur_freq_mhz);
-
-static struct attribute *hl_dev_attrs[] = {
- &dev_attr_clk_max_freq_mhz.attr,
- &dev_attr_clk_cur_freq_mhz.attr,
- NULL,
-};
-
-void hl_add_device_attr(struct hl_device *hdev,
- struct attribute_group *dev_attr_grp)
-{
- dev_attr_grp->attrs = hl_dev_attrs;
-}
diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c
index 1b6bdc900c26..e2bc128f2291 100644
--- a/drivers/misc/habanalabs/common/irq.c
+++ b/drivers/misc/habanalabs/common/irq.c
@@ -137,22 +137,137 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg)
return IRQ_HANDLED;
}
+/*
+ * hl_ts_free_objects - handler of the free objects workqueue.
+ * This function should put refcount to objects that the registration node
+ * took refcount to them.
+ * @work: workqueue object pointer
+ */
+static void hl_ts_free_objects(struct work_struct *work)
+{
+ struct timestamp_reg_work_obj *job =
+ container_of(work, struct timestamp_reg_work_obj, free_obj);
+ struct timestamp_reg_free_node *free_obj, *temp_free_obj;
+ struct list_head *free_list_head = job->free_obj_head;
+ struct hl_device *hdev = job->hdev;
+
+ list_for_each_entry_safe(free_obj, temp_free_obj, free_list_head, free_objects_node) {
+ dev_dbg(hdev->dev, "About to put refcount to ts_buff (%p) cq_cb(%p)\n",
+ free_obj->ts_buff,
+ free_obj->cq_cb);
+
+ hl_ts_put(free_obj->ts_buff);
+ hl_cb_put(free_obj->cq_cb);
+ kfree(free_obj);
+ }
+
+ kfree(free_list_head);
+ kfree(job);
+}
+
+/*
+ * This function called with spin_lock of wait_list_lock taken
+ * This function will set timestamp and delete the registration node from the
+ * wait_list_lock.
+ * and since we're protected with spin_lock here, so we cannot just put the refcount
+ * for the objects here, since the release function may be called and it's also a long
+ * logic (which might sleep also) that cannot be handled in irq context.
+ * so here we'll be filling a list with nodes of "put" jobs and then will send this
+ * list to a dedicated workqueue to do the actual put.
+ */
+static int handle_registration_node(struct hl_device *hdev, struct hl_user_pending_interrupt *pend,
+ struct list_head **free_list)
+{
+ struct timestamp_reg_free_node *free_node;
+ u64 timestamp;
+
+ if (!(*free_list)) {
+ /* Alloc/Init the timestamp registration free objects list */
+ *free_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+ if (!(*free_list))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(*free_list);
+ }
+
+ free_node = kmalloc(sizeof(*free_node), GFP_ATOMIC);
+ if (!free_node)
+ return -ENOMEM;
+
+ timestamp = ktime_get_ns();
+
+ *pend->ts_reg_info.timestamp_kernel_addr = timestamp;
+
+ dev_dbg(hdev->dev, "Timestamp is set to ts cb address (%p), ts: 0x%llx\n",
+ pend->ts_reg_info.timestamp_kernel_addr,
+ *(u64 *)pend->ts_reg_info.timestamp_kernel_addr);
+
+ list_del(&pend->wait_list_node);
+
+ /* Mark kernel CB node as free */
+ pend->ts_reg_info.in_use = 0;
+
+ /* Putting the refcount for ts_buff and cq_cb objects will be handled
+ * in workqueue context, just add job to free_list.
+ */
+ free_node->ts_buff = pend->ts_reg_info.ts_buff;
+ free_node->cq_cb = pend->ts_reg_info.cq_cb;
+ list_add(&free_node->free_objects_node, *free_list);
+
+ return 0;
+}
+
static void handle_user_cq(struct hl_device *hdev,
struct hl_user_interrupt *user_cq)
{
- struct hl_user_pending_interrupt *pend;
+ struct hl_user_pending_interrupt *pend, *temp_pend;
+ struct list_head *ts_reg_free_list_head = NULL;
+ struct timestamp_reg_work_obj *job;
+ bool reg_node_handle_fail = false;
ktime_t now = ktime_get();
+ int rc;
+
+ /* For registration nodes:
+ * As part of handling the registration nodes, we should put refcount to
+ * some objects. the problem is that we cannot do that under spinlock
+ * or in irq handler context at all (since release functions are long and
+ * might sleep), so we will need to handle that part in workqueue context.
+ * To avoid handling kmalloc failure which compels us rolling back actions
+ * and move nodes hanged on the free list back to the interrupt wait list
+ * we always alloc the job of the WQ at the beginning.
+ */
+ job = kmalloc(sizeof(*job), GFP_ATOMIC);
+ if (!job)
+ return;
spin_lock(&user_cq->wait_list_lock);
- list_for_each_entry(pend, &user_cq->wait_list_head, wait_list_node) {
- if ((pend->cq_kernel_addr &&
- *(pend->cq_kernel_addr) >= pend->cq_target_value) ||
+ list_for_each_entry_safe(pend, temp_pend, &user_cq->wait_list_head, wait_list_node) {
+ if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) ||
!pend->cq_kernel_addr) {
- pend->fence.timestamp = now;
- complete_all(&pend->fence.completion);
+ if (pend->ts_reg_info.ts_buff) {
+ if (!reg_node_handle_fail) {
+ rc = handle_registration_node(hdev, pend,
+ &ts_reg_free_list_head);
+ if (rc)
+ reg_node_handle_fail = true;
+ }
+ } else {
+ /* Handle wait target value node */
+ pend->fence.timestamp = now;
+ complete_all(&pend->fence.completion);
+ }
}
}
spin_unlock(&user_cq->wait_list_lock);
+
+ if (ts_reg_free_list_head) {
+ INIT_WORK(&job->free_obj, hl_ts_free_objects);
+ job->free_obj_head = ts_reg_free_list_head;
+ job->hdev = hdev;
+ queue_work(hdev->ts_free_obj_wq, &job->free_obj);
+ } else {
+ kfree(job);
+ }
}
/**
diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c
index c1eefaebacb6..e008d82e4ba3 100644
--- a/drivers/misc/habanalabs/common/memory.c
+++ b/drivers/misc/habanalabs/common/memory.c
@@ -11,6 +11,7 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/pci-p2pdma.h>
MODULE_IMPORT_NS(DMA_BUF);
@@ -20,6 +21,34 @@ MODULE_IMPORT_NS(DMA_BUF);
/* use small pages for supporting non-pow2 (32M/40M/48M) DRAM phys page sizes */
#define DRAM_POOL_PAGE_SIZE SZ_8M
+static int allocate_timestamps_buffers(struct hl_fpriv *hpriv,
+ struct hl_mem_in *args, u64 *handle);
+
+static int set_alloc_page_size(struct hl_device *hdev, struct hl_mem_in *args, u32 *page_size)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 psize;
+
+ /*
+ * for ASIC that supports setting the allocation page size by user we will address
+ * user's choice only if it is not 0 (as 0 means taking the default page size)
+ */
+ if (prop->supports_user_set_page_size && args->alloc.page_size) {
+ psize = args->alloc.page_size;
+
+ if (!hdev->asic_funcs->is_valid_dram_page_size(psize)) {
+ dev_err(hdev->dev, "user page size (%#x) is not valid\n", psize);
+ return -EINVAL;
+ }
+ } else {
+ psize = hdev->asic_prop.dram_page_size;
+ }
+
+ *page_size = psize;
+
+ return 0;
+}
+
/*
* The va ranges in context object contain a list with the available chunks of
* device virtual memory.
@@ -61,11 +90,15 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
struct hl_vm_phys_pg_pack *phys_pg_pack;
u64 paddr = 0, total_size, num_pgs, i;
u32 num_curr_pgs, page_size;
- int handle, rc;
bool contiguous;
+ int handle, rc;
num_curr_pgs = 0;
- page_size = hdev->asic_prop.dram_page_size;
+
+ rc = set_alloc_page_size(hdev, args, &page_size);
+ if (rc)
+ return rc;
+
num_pgs = DIV_ROUND_UP_ULL(args->alloc.mem_size, page_size);
total_size = num_pgs * page_size;
@@ -77,7 +110,11 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
contiguous = args->flags & HL_MEM_CONTIGUOUS;
if (contiguous) {
- paddr = (u64) gen_pool_alloc(vm->dram_pg_pool, total_size);
+ if (is_power_of_2(page_size))
+ paddr = (u64) (uintptr_t) gen_pool_dma_alloc_align(vm->dram_pg_pool,
+ total_size, NULL, page_size);
+ else
+ paddr = (u64) (uintptr_t) gen_pool_alloc(vm->dram_pg_pool, total_size);
if (!paddr) {
dev_err(hdev->dev,
"failed to allocate %llu contiguous pages with total size of %llu\n",
@@ -111,9 +148,14 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
phys_pg_pack->pages[i] = paddr + i * page_size;
} else {
for (i = 0 ; i < num_pgs ; i++) {
- phys_pg_pack->pages[i] = (u64) gen_pool_alloc(
- vm->dram_pg_pool,
- page_size);
+ if (is_power_of_2(page_size))
+ phys_pg_pack->pages[i] =
+ (u64) gen_pool_dma_alloc_align(vm->dram_pg_pool,
+ page_size, NULL,
+ page_size);
+ else
+ phys_pg_pack->pages[i] = (u64) gen_pool_alloc(vm->dram_pg_pool,
+ page_size);
if (!phys_pg_pack->pages[i]) {
dev_err(hdev->dev,
"Failed to allocate device memory (out of memory)\n");
@@ -652,7 +694,7 @@ static u64 get_va_block(struct hl_device *hdev,
continue;
/*
- * In case hint address is 0, and arc_hints_range_reservation
+ * In case hint address is 0, and hints_range_reservation
* property enabled, then avoid allocating va blocks from the
* range reserved for hint addresses
*/
@@ -1967,16 +2009,15 @@ err_dec_exporting_cnt:
static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args)
{
struct hl_device *hdev = hpriv->hdev;
- struct hl_ctx *ctx = hpriv->ctx;
u64 block_handle, device_addr = 0;
+ struct hl_ctx *ctx = hpriv->ctx;
u32 handle = 0, block_size;
- int rc, dmabuf_fd = -EBADF;
+ int rc;
switch (args->in.op) {
case HL_MEM_OP_ALLOC:
if (args->in.alloc.mem_size == 0) {
- dev_err(hdev->dev,
- "alloc size must be larger than 0\n");
+ dev_err(hdev->dev, "alloc size must be larger than 0\n");
rc = -EINVAL;
goto out;
}
@@ -1997,15 +2038,14 @@ static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args)
case HL_MEM_OP_MAP:
if (args->in.flags & HL_MEM_USERPTR) {
- device_addr = args->in.map_host.host_virt_addr;
- rc = 0;
+ dev_err(hdev->dev, "Failed to map host memory when MMU is disabled\n");
+ rc = -EPERM;
} else {
- rc = get_paddr_from_handle(ctx, &args->in,
- &device_addr);
+ rc = get_paddr_from_handle(ctx, &args->in, &device_addr);
+ memset(args, 0, sizeof(*args));
+ args->out.device_virt_addr = device_addr;
}
- memset(args, 0, sizeof(*args));
- args->out.device_virt_addr = device_addr;
break;
case HL_MEM_OP_UNMAP:
@@ -2013,22 +2053,19 @@ static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args)
break;
case HL_MEM_OP_MAP_BLOCK:
- rc = map_block(hdev, args->in.map_block.block_addr,
- &block_handle, &block_size);
+ rc = map_block(hdev, args->in.map_block.block_addr, &block_handle, &block_size);
args->out.block_handle = block_handle;
args->out.block_size = block_size;
break;
case HL_MEM_OP_EXPORT_DMABUF_FD:
- rc = export_dmabuf_from_addr(ctx,
- args->in.export_dmabuf_fd.handle,
- args->in.export_dmabuf_fd.mem_size,
- args->in.flags,
- &dmabuf_fd);
- memset(args, 0, sizeof(*args));
- args->out.fd = dmabuf_fd;
+ dev_err(hdev->dev, "Failed to export dma-buf object when MMU is disabled\n");
+ rc = -EPERM;
break;
+ case HL_MEM_OP_TS_ALLOC:
+ rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle);
+ break;
default:
dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n");
rc = -EINVAL;
@@ -2039,6 +2076,258 @@ out:
return rc;
}
+static void ts_buff_release(struct kref *ref)
+{
+ struct hl_ts_buff *buff;
+
+ buff = container_of(ref, struct hl_ts_buff, refcount);
+
+ vfree(buff->kernel_buff_address);
+ vfree(buff->user_buff_address);
+ kfree(buff);
+}
+
+struct hl_ts_buff *hl_ts_get(struct hl_device *hdev, struct hl_ts_mgr *mgr,
+ u32 handle)
+{
+ struct hl_ts_buff *buff;
+
+ spin_lock(&mgr->ts_lock);
+ buff = idr_find(&mgr->ts_handles, handle);
+ if (!buff) {
+ spin_unlock(&mgr->ts_lock);
+ dev_warn(hdev->dev,
+ "TS buff get failed, no match to handle 0x%x\n", handle);
+ return NULL;
+ }
+ kref_get(&buff->refcount);
+ spin_unlock(&mgr->ts_lock);
+
+ return buff;
+}
+
+void hl_ts_put(struct hl_ts_buff *buff)
+{
+ kref_put(&buff->refcount, ts_buff_release);
+}
+
+static void buff_vm_close(struct vm_area_struct *vma)
+{
+ struct hl_ts_buff *buff = (struct hl_ts_buff *) vma->vm_private_data;
+ long new_mmap_size;
+
+ new_mmap_size = buff->mmap_size - (vma->vm_end - vma->vm_start);
+
+ if (new_mmap_size > 0) {
+ buff->mmap_size = new_mmap_size;
+ return;
+ }
+
+ atomic_set(&buff->mmap, 0);
+ hl_ts_put(buff);
+ vma->vm_private_data = NULL;
+}
+
+static const struct vm_operations_struct ts_buff_vm_ops = {
+ .close = buff_vm_close
+};
+
+int hl_ts_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_ts_buff *buff;
+ u32 handle, user_buff_size;
+ int rc;
+
+ /* We use the page offset to hold the idr and thus we need to clear
+ * it before doing the mmap itself
+ */
+ handle = vma->vm_pgoff;
+ vma->vm_pgoff = 0;
+
+ buff = hl_ts_get(hdev, &hpriv->ts_mem_mgr, handle);
+ if (!buff) {
+ dev_err(hdev->dev,
+ "TS buff mmap failed, no match to handle 0x%x\n", handle);
+ return -EINVAL;
+ }
+
+ /* Validation check */
+ user_buff_size = vma->vm_end - vma->vm_start;
+ if (user_buff_size != ALIGN(buff->user_buff_size, PAGE_SIZE)) {
+ dev_err(hdev->dev,
+ "TS buff mmap failed, mmap size 0x%x != 0x%x buff size\n",
+ user_buff_size, ALIGN(buff->user_buff_size, PAGE_SIZE));
+ rc = -EINVAL;
+ goto put_buff;
+ }
+
+#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK
+ if (!access_ok(VERIFY_WRITE,
+ (void __user *) (uintptr_t) vma->vm_start, user_buff_size)) {
+#else
+ if (!access_ok((void __user *) (uintptr_t) vma->vm_start,
+ user_buff_size)) {
+#endif
+ dev_err(hdev->dev,
+ "user pointer is invalid - 0x%lx\n",
+ vma->vm_start);
+
+ rc = -EINVAL;
+ goto put_buff;
+ }
+
+ if (atomic_cmpxchg(&buff->mmap, 0, 1)) {
+ dev_err(hdev->dev, "TS buff memory mmap failed, already mmaped to user\n");
+ rc = -EINVAL;
+ goto put_buff;
+ }
+
+ vma->vm_ops = &ts_buff_vm_ops;
+ vma->vm_private_data = buff;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY | VM_NORESERVE;
+ rc = remap_vmalloc_range(vma, buff->user_buff_address, 0);
+ if (rc) {
+ atomic_set(&buff->mmap, 0);
+ goto put_buff;
+ }
+
+ buff->mmap_size = buff->user_buff_size;
+ vma->vm_pgoff = handle;
+
+ return 0;
+
+put_buff:
+ hl_ts_put(buff);
+ return rc;
+}
+
+void hl_ts_mgr_init(struct hl_ts_mgr *mgr)
+{
+ spin_lock_init(&mgr->ts_lock);
+ idr_init(&mgr->ts_handles);
+}
+
+void hl_ts_mgr_fini(struct hl_device *hdev, struct hl_ts_mgr *mgr)
+{
+ struct hl_ts_buff *buff;
+ struct idr *idp;
+ u32 id;
+
+ idp = &mgr->ts_handles;
+
+ idr_for_each_entry(idp, buff, id) {
+ if (kref_put(&buff->refcount, ts_buff_release) != 1)
+ dev_err(hdev->dev, "TS buff handle %d for CTX is still alive\n",
+ id);
+ }
+
+ idr_destroy(&mgr->ts_handles);
+}
+
+static struct hl_ts_buff *hl_ts_alloc_buff(struct hl_device *hdev, u32 num_elements)
+{
+ struct hl_ts_buff *ts_buff = NULL;
+ u32 size;
+ void *p;
+
+ ts_buff = kzalloc(sizeof(*ts_buff), GFP_KERNEL);
+ if (!ts_buff)
+ return NULL;
+
+ /* Allocate the user buffer */
+ size = num_elements * sizeof(u64);
+ p = vmalloc_user(size);
+ if (!p)
+ goto free_mem;
+
+ ts_buff->user_buff_address = p;
+ ts_buff->user_buff_size = size;
+
+ /* Allocate the internal kernel buffer */
+ size = num_elements * sizeof(struct hl_user_pending_interrupt);
+ p = vmalloc(size);
+ if (!p)
+ goto free_user_buff;
+
+ ts_buff->kernel_buff_address = p;
+ ts_buff->kernel_buff_size = size;
+
+ return ts_buff;
+
+free_user_buff:
+ vfree(ts_buff->user_buff_address);
+free_mem:
+ kfree(ts_buff);
+ return NULL;
+}
+
+/**
+ * allocate_timestamps_buffers() - allocate timestamps buffers
+ * This function will allocate ts buffer that will later on be mapped to the user
+ * in order to be able to read the timestamp.
+ * in additon it'll allocate an extra buffer for registration management.
+ * since we cannot fail during registration for out-of-memory situation, so
+ * we'll prepare a pool which will be used as user interrupt nodes and instead
+ * of dynamically allocating nodes while registration we'll pick the node from
+ * this pool. in addtion it'll add node to the mapping hash which will be used
+ * to map user ts buffer to the internal kernel ts buffer.
+ * @hpriv: pointer to the private data of the fd
+ * @args: ioctl input
+ * @handle: user timestamp buffer handle as an output
+ */
+static int allocate_timestamps_buffers(struct hl_fpriv *hpriv, struct hl_mem_in *args, u64 *handle)
+{
+ struct hl_ts_mgr *ts_mgr = &hpriv->ts_mem_mgr;
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_ts_buff *ts_buff;
+ int rc = 0;
+
+ if (args->num_of_elements > TS_MAX_ELEMENTS_NUM) {
+ dev_err(hdev->dev, "Num of elements exceeds Max allowed number (0x%x > 0x%x)\n",
+ args->num_of_elements, TS_MAX_ELEMENTS_NUM);
+ return -EINVAL;
+ }
+
+ /* Allocate ts buffer object
+ * This object will contain two buffers one that will be mapped to the user
+ * and another internal buffer for the driver use only, which won't be mapped
+ * to the user.
+ */
+ ts_buff = hl_ts_alloc_buff(hdev, args->num_of_elements);
+ if (!ts_buff) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+
+ spin_lock(&ts_mgr->ts_lock);
+ rc = idr_alloc(&ts_mgr->ts_handles, ts_buff, 1, 0, GFP_ATOMIC);
+ spin_unlock(&ts_mgr->ts_lock);
+ if (rc < 0) {
+ dev_err(hdev->dev, "Failed to allocate IDR for a new ts buffer\n");
+ goto release_ts_buff;
+ }
+
+ ts_buff->id = rc;
+ ts_buff->hdev = hdev;
+
+ kref_init(&ts_buff->refcount);
+
+ /* idr is 32-bit so we can safely OR it with a mask that is above 32 bit */
+ *handle = (u64) ts_buff->id | HL_MMAP_TYPE_TS_BUFF;
+ *handle <<= PAGE_SHIFT;
+
+ dev_dbg(hdev->dev, "Created ts buff object handle(%u)\n", ts_buff->id);
+
+ return 0;
+
+release_ts_buff:
+ kref_put(&ts_buff->refcount, ts_buff_release);
+out_err:
+ *handle = 0;
+ return rc;
+}
+
int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
{
enum hl_device_status status;
@@ -2154,6 +2443,9 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
args->out.fd = dmabuf_fd;
break;
+ case HL_MEM_OP_TS_ALLOC:
+ rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle);
+ break;
default:
dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n");
rc = -EINVAL;
@@ -2607,11 +2899,12 @@ int hl_vm_ctx_init(struct hl_ctx *ctx)
*/
void hl_vm_ctx_fini(struct hl_ctx *ctx)
{
+ struct hl_vm_phys_pg_pack *phys_pg_list, *tmp_phys_node;
struct hl_device *hdev = ctx->hdev;
- struct hl_vm *vm = &hdev->vm;
- struct hl_vm_phys_pg_pack *phys_pg_list;
struct hl_vm_hash_node *hnode;
+ struct hl_vm *vm = &hdev->vm;
struct hlist_node *tmp_node;
+ struct list_head free_list;
struct hl_mem_in args;
int i;
@@ -2644,19 +2937,24 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx)
mutex_unlock(&ctx->mmu_lock);
+ INIT_LIST_HEAD(&free_list);
+
spin_lock(&vm->idr_lock);
idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i)
if (phys_pg_list->asid == ctx->asid) {
dev_dbg(hdev->dev,
"page list 0x%px of asid %d is still alive\n",
phys_pg_list, ctx->asid);
- atomic64_sub(phys_pg_list->total_size,
- &hdev->dram_used_mem);
- free_phys_pg_pack(hdev, phys_pg_list);
+
+ atomic64_sub(phys_pg_list->total_size, &hdev->dram_used_mem);
idr_remove(&vm->phys_pg_pack_handles, i);
+ list_add(&phys_pg_list->node, &free_list);
}
spin_unlock(&vm->idr_lock);
+ list_for_each_entry_safe(phys_pg_list, tmp_phys_node, &free_list, node)
+ free_phys_pg_pack(hdev, phys_pg_list);
+
va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_DRAM]);
va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_HOST]);
diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c
index 9153a1f55175..810b73421ce1 100644
--- a/drivers/misc/habanalabs/common/mmu/mmu.c
+++ b/drivers/misc/habanalabs/common/mmu/mmu.c
@@ -662,3 +662,58 @@ int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,
return rc;
}
+u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte)
+{
+ return (curr_pte & PAGE_PRESENT_MASK) ? (curr_pte & HOP_PHYS_ADDR_MASK) : ULLONG_MAX;
+}
+
+/**
+ * hl_mmu_get_hop_pte_phys_addr() - extract PTE address from HOP
+ * @ctx: pointer to the context structure to initialize.
+ * @hop_idx: HOP index.
+ * @hop_addr: HOP address.
+ * @virt_addr: virtual address fro the translation.
+ *
+ * @return the matching PTE value on success, otherwise U64_MAX.
+ */
+u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,
+ u8 hop_idx, u64 hop_addr, u64 virt_addr)
+{
+ u64 mask, shift;
+
+ if (hop_idx >= mmu_prop->num_hops) {
+ dev_err_ratelimited(ctx->hdev->dev, "Invalid hop index %d\n", hop_idx);
+ return U64_MAX;
+ }
+
+ /* currently max number of HOPs is 6 */
+ switch (hop_idx) {
+ case 0:
+ mask = mmu_prop->hop0_mask;
+ shift = mmu_prop->hop0_shift;
+ break;
+ case 1:
+ mask = mmu_prop->hop1_mask;
+ shift = mmu_prop->hop1_shift;
+ break;
+ case 2:
+ mask = mmu_prop->hop2_mask;
+ shift = mmu_prop->hop2_shift;
+ break;
+ case 3:
+ mask = mmu_prop->hop3_mask;
+ shift = mmu_prop->hop3_shift;
+ break;
+ case 4:
+ mask = mmu_prop->hop4_mask;
+ shift = mmu_prop->hop4_shift;
+ break;
+ default:
+ mask = mmu_prop->hop5_mask;
+ shift = mmu_prop->hop5_shift;
+ break;
+ }
+
+ return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * ((virt_addr & mask) >> shift);
+}
+
diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v1.c b/drivers/misc/habanalabs/common/mmu/mmu_v1.c
index 6134b6ae7615..d03786d0c407 100644
--- a/drivers/misc/habanalabs/common/mmu/mmu_v1.c
+++ b/drivers/misc/habanalabs/common/mmu/mmu_v1.c
@@ -217,18 +217,10 @@ static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx,
mmu_prop->hop4_shift);
}
-static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte)
-{
- if (curr_pte & PAGE_PRESENT_MASK)
- return curr_pte & HOP_PHYS_ADDR_MASK;
- else
- return ULLONG_MAX;
-}
-
static inline u64 get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte,
bool *is_new_hop)
{
- u64 hop_addr = get_next_hop_addr(ctx, curr_pte);
+ u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
if (hop_addr == ULLONG_MAX) {
hop_addr = alloc_hop(ctx);
@@ -467,7 +459,7 @@ static void hl_mmu_v1_fini(struct hl_device *hdev)
{
/* MMU H/W fini was already done in device hw_fini() */
- if (!ZERO_OR_NULL_PTR(hdev->mmu_priv.hr.mmu_shadow_hop0)) {
+ if (!ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0)) {
kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0);
gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
@@ -546,7 +538,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx,
curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr;
- hop1_addr = get_next_hop_addr(ctx, curr_pte);
+ hop1_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
if (hop1_addr == ULLONG_MAX)
goto not_mapped;
@@ -555,7 +547,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx,
curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr;
- hop2_addr = get_next_hop_addr(ctx, curr_pte);
+ hop2_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
if (hop2_addr == ULLONG_MAX)
goto not_mapped;
@@ -564,7 +556,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx,
curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr;
- hop3_addr = get_next_hop_addr(ctx, curr_pte);
+ hop3_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
if (hop3_addr == ULLONG_MAX)
goto not_mapped;
@@ -582,7 +574,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx,
}
if (!is_huge) {
- hop4_addr = get_next_hop_addr(ctx, curr_pte);
+ hop4_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
if (hop4_addr == ULLONG_MAX)
goto not_mapped;
@@ -845,27 +837,6 @@ static void hl_mmu_v1_swap_in(struct hl_ctx *ctx)
}
-static inline u64 get_hop_pte_addr(struct hl_ctx *ctx,
- struct hl_mmu_properties *mmu_prop,
- int hop_num, u64 hop_addr, u64 virt_addr)
-{
- switch (hop_num) {
- case 0:
- return get_hop0_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
- case 1:
- return get_hop1_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
- case 2:
- return get_hop2_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
- case 3:
- return get_hop3_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
- case 4:
- return get_hop4_pte_addr(ctx, mmu_prop, hop_addr, virt_addr);
- default:
- break;
- }
- return U64_MAX;
-}
-
static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
struct hl_mmu_hop_info *hops)
{
@@ -906,7 +877,7 @@ static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
hops->hop_info[0].hop_addr = get_phys_hop0_addr(ctx);
hops->hop_info[0].hop_pte_addr =
- get_hop_pte_addr(ctx, mmu_prop, 0,
+ hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,
hops->hop_info[0].hop_addr, virt_addr);
hops->hop_info[0].hop_pte_val =
hdev->asic_funcs->read_pte(hdev,
@@ -914,13 +885,13 @@ static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
for (i = 1 ; i < used_hops ; i++) {
hops->hop_info[i].hop_addr =
- get_next_hop_addr(ctx,
+ hl_mmu_get_next_hop_addr(ctx,
hops->hop_info[i - 1].hop_pte_val);
if (hops->hop_info[i].hop_addr == ULLONG_MAX)
return -EFAULT;
hops->hop_info[i].hop_pte_addr =
- get_hop_pte_addr(ctx, mmu_prop, i,
+ hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
hops->hop_info[i].hop_addr,
virt_addr);
hops->hop_info[i].hop_pte_val =
diff --git a/drivers/misc/habanalabs/common/pci/pci.c b/drivers/misc/habanalabs/common/pci/pci.c
index 0b5366cc84fd..bb9ce22bafc4 100644
--- a/drivers/misc/habanalabs/common/pci/pci.c
+++ b/drivers/misc/habanalabs/common/pci/pci.c
@@ -338,10 +338,7 @@ int hl_pci_set_outbound_region(struct hl_device *hdev,
lower_32_bits(outbound_region_end_address));
rc |= hl_pci_iatu_write(hdev, 0x014, 0);
- if ((hdev->power9_64bit_dma_enable) && (hdev->dma_mask == 64))
- rc |= hl_pci_iatu_write(hdev, 0x018, 0x08000000);
- else
- rc |= hl_pci_iatu_write(hdev, 0x018, 0);
+ rc |= hl_pci_iatu_write(hdev, 0x018, 0);
rc |= hl_pci_iatu_write(hdev, 0x020,
upper_32_bits(outbound_region_end_address));
@@ -411,13 +408,13 @@ int hl_pci_init(struct hl_device *hdev)
rc = hdev->asic_funcs->pci_bars_map(hdev);
if (rc) {
- dev_err(hdev->dev, "Failed to initialize PCI BARs\n");
+ dev_err(hdev->dev, "Failed to map PCI BAR addresses\n");
goto disable_device;
}
rc = hdev->asic_funcs->init_iatu(hdev);
if (rc) {
- dev_err(hdev->dev, "Failed to initialize iATU\n");
+ dev_err(hdev->dev, "PCI controller was not initialized successfully\n");
goto unmap_pci_bars;
}
diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c
index 45c715325e2a..9ebeb18ab85e 100644
--- a/drivers/misc/habanalabs/common/sysfs.c
+++ b/drivers/misc/habanalabs/common/sysfs.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -9,105 +9,91 @@
#include <linux/pci.h>
-long hl_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr)
+static ssize_t clk_max_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct cpucp_packet pkt;
- u32 used_pll_idx;
- u64 result;
- int rc;
-
- rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
- if (rc)
- return rc;
-
- memset(&pkt, 0, sizeof(pkt));
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long value;
- if (curr)
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_CURR_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- else
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
+ value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
+ if (value < 0)
+ return value;
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get frequency of PLL %d, error %d\n",
- used_pll_idx, rc);
- return rc;
- }
+ hdev->asic_prop.max_freq_value = value;
- return (long) result;
+ return sprintf(buf, "%lu\n", (value / 1000 / 1000));
}
-void hl_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq)
+static ssize_t clk_max_freq_mhz_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct cpucp_packet pkt;
- u32 used_pll_idx;
+ struct hl_device *hdev = dev_get_drvdata(dev);
int rc;
+ u64 value;
- rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
- if (rc)
- return;
+ if (!hl_device_operational(hdev, NULL)) {
+ count = -ENODEV;
+ goto fail;
+ }
- memset(&pkt, 0, sizeof(pkt));
+ rc = kstrtoull(buf, 0, &value);
+ if (rc) {
+ count = -EINVAL;
+ goto fail;
+ }
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
- pkt.value = cpu_to_le64(freq);
+ hdev->asic_prop.max_freq_value = value * 1000 * 1000;
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
+ hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index, hdev->asic_prop.max_freq_value);
- if (rc)
- dev_err(hdev->dev,
- "Failed to set frequency to PLL %d, error %d\n",
- used_pll_idx, rc);
+fail:
+ return count;
}
-u64 hl_get_max_power(struct hl_device *hdev)
+static ssize_t clk_cur_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long value;
- memset(&pkt, 0, sizeof(pkt));
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
+ value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
+ if (value < 0)
+ return value;
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
+ return sprintf(buf, "%lu\n", (value / 1000 / 1000));
+}
- if (rc) {
- dev_err(hdev->dev, "Failed to get max power, error %d\n", rc);
- return (u64) rc;
- }
+static DEVICE_ATTR_RW(clk_max_freq_mhz);
+static DEVICE_ATTR_RO(clk_cur_freq_mhz);
- return result;
-}
+static struct attribute *hl_dev_clk_attrs[] = {
+ &dev_attr_clk_max_freq_mhz.attr,
+ &dev_attr_clk_cur_freq_mhz.attr,
+};
-void hl_set_max_power(struct hl_device *hdev)
+static ssize_t vrm_ver_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct cpucp_packet pkt;
- int rc;
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ struct cpucp_info *cpucp_info;
- memset(&pkt, 0, sizeof(pkt));
+ cpucp_info = &hdev->asic_prop.cpucp_info;
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.value = cpu_to_le64(hdev->max_power);
+ if (cpucp_info->infineon_second_stage_version)
+ return sprintf(buf, "%#04x %#04x\n", le32_to_cpu(cpucp_info->infineon_version),
+ le32_to_cpu(cpucp_info->infineon_second_stage_version));
+ else
+ return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version));
+}
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
+static DEVICE_ATTR_RO(vrm_ver);
- if (rc)
- dev_err(hdev->dev, "Failed to set max power, error %d\n", rc);
-}
+static struct attribute *hl_dev_vrm_attrs[] = {
+ &dev_attr_vrm_ver.attr,
+};
static ssize_t uboot_ver_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -158,20 +144,6 @@ static ssize_t cpucp_ver_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.cpucp_version);
}
-static ssize_t infineon_ver_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- if (hdev->asic_prop.cpucp_info.infineon_second_stage_version)
- return sprintf(buf, "%#04x %#04x\n",
- le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_version),
- le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_second_stage_version));
- else
- return sprintf(buf, "%#04x\n",
- le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_version));
-}
-
static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -188,6 +160,14 @@ static ssize_t thermal_ver_show(struct device *dev,
return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.thermal_version);
}
+static ssize_t fw_os_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.fw_os_version);
+}
+
static ssize_t preboot_btl_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -323,7 +303,9 @@ static ssize_t max_power_show(struct device *dev, struct device_attribute *attr,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- val = hl_get_max_power(hdev);
+ val = hl_fw_get_max_power(hdev);
+ if (val < 0)
+ return val;
return sprintf(buf, "%lu\n", val);
}
@@ -348,7 +330,7 @@ static ssize_t max_power_store(struct device *dev,
}
hdev->max_power = value;
- hl_set_max_power(hdev);
+ hl_fw_set_max_power(hdev);
out:
return count;
@@ -394,7 +376,6 @@ static DEVICE_ATTR_RO(device_type);
static DEVICE_ATTR_RO(fuse_ver);
static DEVICE_ATTR_WO(hard_reset);
static DEVICE_ATTR_RO(hard_reset_cnt);
-static DEVICE_ATTR_RO(infineon_ver);
static DEVICE_ATTR_RW(max_power);
static DEVICE_ATTR_RO(pci_addr);
static DEVICE_ATTR_RO(preboot_btl_ver);
@@ -403,6 +384,7 @@ static DEVICE_ATTR_RO(soft_reset_cnt);
static DEVICE_ATTR_RO(status);
static DEVICE_ATTR_RO(thermal_ver);
static DEVICE_ATTR_RO(uboot_ver);
+static DEVICE_ATTR_RO(fw_os_ver);
static struct bin_attribute bin_attr_eeprom = {
.attr = {.name = "eeprom", .mode = (0444)},
@@ -420,13 +402,13 @@ static struct attribute *hl_dev_attrs[] = {
&dev_attr_fuse_ver.attr,
&dev_attr_hard_reset.attr,
&dev_attr_hard_reset_cnt.attr,
- &dev_attr_infineon_ver.attr,
&dev_attr_max_power.attr,
&dev_attr_pci_addr.attr,
&dev_attr_preboot_btl_ver.attr,
&dev_attr_status.attr,
&dev_attr_thermal_ver.attr,
&dev_attr_uboot_ver.attr,
+ &dev_attr_fw_os_ver.attr,
NULL,
};
@@ -441,10 +423,12 @@ static struct attribute_group hl_dev_attr_group = {
};
static struct attribute_group hl_dev_clks_attr_group;
+static struct attribute_group hl_dev_vrm_attr_group;
static const struct attribute_group *hl_dev_attr_groups[] = {
&hl_dev_attr_group,
&hl_dev_clks_attr_group,
+ &hl_dev_vrm_attr_group,
NULL,
};
@@ -463,13 +447,23 @@ static const struct attribute_group *hl_dev_inference_attr_groups[] = {
NULL,
};
+void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp)
+{
+ dev_clk_attr_grp->attrs = hl_dev_clk_attrs;
+}
+
+void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp)
+{
+ dev_vrm_attr_grp->attrs = hl_dev_vrm_attrs;
+}
+
int hl_sysfs_init(struct hl_device *hdev)
{
int rc;
hdev->max_power = hdev->asic_prop.max_power_default;
- hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group);
+ hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group, &hl_dev_vrm_attr_group);
rc = device_add_groups(hdev->dev, hl_dev_attr_groups);
if (rc) {
diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c
index 013c6da2e3ca..21c2b678ff72 100644
--- a/drivers/misc/habanalabs/gaudi/gaudi.c
+++ b/drivers/misc/habanalabs/gaudi/gaudi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -458,7 +458,6 @@ struct ecc_info_extract_params {
u64 block_address;
u32 num_memories;
bool derr;
- bool disable_clock_gating;
};
static int gaudi_mmu_update_asid_hop0_addr(struct hl_device *hdev, u32 asid,
@@ -614,6 +613,9 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
prop->pmmu.page_size = PAGE_SIZE_4KB;
prop->pmmu.num_hops = MMU_ARCH_5_HOPS;
prop->pmmu.last_mask = LAST_MASK;
+ /* TODO: will be duplicated until implementing per-MMU props */
+ prop->pmmu.hop_table_size = prop->mmu_hop_table_size;
+ prop->pmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size;
/* PMMU and HPMMU are the same except of page size */
memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu));
@@ -667,6 +669,10 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
prop->use_get_power_for_reset_history = true;
+ prop->configurable_stop_on_err = true;
+
+ prop->set_max_power_on_device_init = true;
+
return 0;
}
@@ -1636,7 +1642,7 @@ static int gaudi_late_init(struct hl_device *hdev)
*/
gaudi_mmu_prepare(hdev, 1);
- hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST);
+ hl_fw_set_pll_profile(hdev);
return 0;
@@ -1896,7 +1902,6 @@ static int gaudi_sw_init(struct hl_device *hdev)
goto free_cpu_accessible_dma_pool;
spin_lock_init(&gaudi->hw_queues_lock);
- mutex_init(&gaudi->clk_gate_mutex);
hdev->supports_sync_stream = true;
hdev->supports_coresight = true;
@@ -1946,8 +1951,6 @@ static int gaudi_sw_fini(struct hl_device *hdev)
dma_pool_destroy(hdev->dma_pool);
- mutex_destroy(&gaudi->clk_gate_mutex);
-
kfree(gaudi);
return 0;
@@ -3738,76 +3741,8 @@ static void gaudi_tpc_stall(struct hl_device *hdev)
WREG32(mmTPC7_CFG_TPC_STALL, 1 << TPC0_CFG_TPC_STALL_V_SHIFT);
}
-static void gaudi_set_clock_gating(struct hl_device *hdev)
-{
- struct gaudi_device *gaudi = hdev->asic_specific;
- u32 qman_offset;
- bool enable;
- int i;
-
- /* In case we are during debug session, don't enable the clock gate
- * as it may interfere
- */
- if (hdev->in_debug)
- return;
-
- if (hdev->asic_prop.fw_security_enabled)
- return;
-
- for (i = GAUDI_PCI_DMA_1, qman_offset = 0 ; i < GAUDI_HBM_DMA_1 ; i++) {
- enable = !!(hdev->clock_gating_mask &
- (BIT_ULL(gaudi_dma_assignment[i])));
-
- qman_offset = gaudi_dma_assignment[i] * DMA_QMAN_OFFSET;
- WREG32(mmDMA0_QM_CGM_CFG1 + qman_offset,
- enable ? QMAN_CGM1_PWR_GATE_EN : 0);
- WREG32(mmDMA0_QM_CGM_CFG + qman_offset,
- enable ? QMAN_UPPER_CP_CGM_PWR_GATE_EN : 0);
- }
-
- for (i = GAUDI_HBM_DMA_1 ; i < GAUDI_DMA_MAX ; i++) {
- enable = !!(hdev->clock_gating_mask &
- (BIT_ULL(gaudi_dma_assignment[i])));
-
- /* GC sends work to DMA engine through Upper CP in DMA5 so
- * we need to not enable clock gating in that DMA
- */
- if (i == GAUDI_HBM_DMA_4)
- enable = 0;
-
- qman_offset = gaudi_dma_assignment[i] * DMA_QMAN_OFFSET;
- WREG32(mmDMA0_QM_CGM_CFG1 + qman_offset,
- enable ? QMAN_CGM1_PWR_GATE_EN : 0);
- WREG32(mmDMA0_QM_CGM_CFG + qman_offset,
- enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0);
- }
-
- enable = !!(hdev->clock_gating_mask & (BIT_ULL(GAUDI_ENGINE_ID_MME_0)));
- WREG32(mmMME0_QM_CGM_CFG1, enable ? QMAN_CGM1_PWR_GATE_EN : 0);
- WREG32(mmMME0_QM_CGM_CFG, enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0);
-
- enable = !!(hdev->clock_gating_mask & (BIT_ULL(GAUDI_ENGINE_ID_MME_2)));
- WREG32(mmMME2_QM_CGM_CFG1, enable ? QMAN_CGM1_PWR_GATE_EN : 0);
- WREG32(mmMME2_QM_CGM_CFG, enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0);
-
- for (i = 0, qman_offset = 0 ; i < TPC_NUMBER_OF_ENGINES ; i++) {
- enable = !!(hdev->clock_gating_mask &
- (BIT_ULL(GAUDI_ENGINE_ID_TPC_0 + i)));
-
- WREG32(mmTPC0_QM_CGM_CFG1 + qman_offset,
- enable ? QMAN_CGM1_PWR_GATE_EN : 0);
- WREG32(mmTPC0_QM_CGM_CFG + qman_offset,
- enable ? QMAN_COMMON_CP_CGM_PWR_GATE_EN : 0);
-
- qman_offset += TPC_QMAN_OFFSET;
- }
-
- gaudi->hw_cap_initialized |= HW_CAP_CLK_GATE;
-}
-
static void gaudi_disable_clock_gating(struct hl_device *hdev)
{
- struct gaudi_device *gaudi = hdev->asic_specific;
u32 qman_offset;
int i;
@@ -3832,8 +3767,6 @@ static void gaudi_disable_clock_gating(struct hl_device *hdev)
qman_offset += (mmTPC1_QM_CGM_CFG - mmTPC0_QM_CGM_CFG);
}
-
- gaudi->hw_cap_initialized &= ~(HW_CAP_CLK_GATE);
}
static void gaudi_enable_timestamp(struct hl_device *hdev)
@@ -3876,8 +3809,6 @@ static void gaudi_halt_engines(struct hl_device *hdev, bool hard_reset, bool fw_
gaudi_stop_hbm_dma_qmans(hdev);
gaudi_stop_pci_dma_qmans(hdev);
- hdev->asic_funcs->disable_clock_gating(hdev);
-
msleep(wait_timeout_ms);
gaudi_pci_dma_stall(hdev);
@@ -3931,7 +3862,7 @@ static int gaudi_mmu_init(struct hl_device *hdev)
/* mem cache invalidation */
WREG32(mmSTLB_MEM_CACHE_INVALIDATION, 1);
- hdev->asic_funcs->mmu_invalidate_cache(hdev, true, 0);
+ hl_mmu_invalidate_cache(hdev, true, 0);
WREG32(mmMMU_UP_MMU_ENABLE, 1);
WREG32(mmMMU_UP_SPI_MASK, 0xF);
@@ -4203,10 +4134,8 @@ static int gaudi_hw_init(struct hl_device *hdev)
/* In case the clock gating was enabled in preboot we need to disable
* it here before touching the MME/TPC registers.
- * There is no need to take clk gating mutex because when this function
- * runs, no other relevant code can run
*/
- hdev->asic_funcs->disable_clock_gating(hdev);
+ gaudi_disable_clock_gating(hdev);
/* SRAM scrambler must be initialized after CPU is running from HBM */
gaudi_init_scrambler_sram(hdev);
@@ -4232,8 +4161,6 @@ static int gaudi_hw_init(struct hl_device *hdev)
gaudi_init_nic_qmans(hdev);
- hdev->asic_funcs->set_clock_gating(hdev);
-
gaudi_enable_timestamp(hdev);
/* MSI must be enabled before CPU queues and NIC are initialized */
@@ -4400,14 +4327,11 @@ skip_reset:
status);
if (gaudi) {
- gaudi->hw_cap_initialized &= ~(HW_CAP_CPU | HW_CAP_CPU_Q |
- HW_CAP_HBM | HW_CAP_PCI_DMA |
- HW_CAP_MME | HW_CAP_TPC_MASK |
- HW_CAP_HBM_DMA | HW_CAP_PLL |
- HW_CAP_NIC_MASK | HW_CAP_MMU |
- HW_CAP_SRAM_SCRAMBLER |
- HW_CAP_HBM_SCRAMBLER |
- HW_CAP_CLK_GATE);
+ gaudi->hw_cap_initialized &= ~(HW_CAP_CPU | HW_CAP_CPU_Q | HW_CAP_HBM |
+ HW_CAP_PCI_DMA | HW_CAP_MME | HW_CAP_TPC_MASK |
+ HW_CAP_HBM_DMA | HW_CAP_PLL | HW_CAP_NIC_MASK |
+ HW_CAP_MMU | HW_CAP_SRAM_SCRAMBLER |
+ HW_CAP_HBM_SCRAMBLER);
memset(gaudi->events_stat, 0, sizeof(gaudi->events_stat));
@@ -4884,7 +4808,6 @@ static int gaudi_hbm_scrubbing(struct hl_device *hdev)
static int gaudi_scrub_device_mem(struct hl_device *hdev, u64 addr, u64 size)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct gaudi_device *gaudi = hdev->asic_specific;
int rc = 0;
u64 val = 0;
@@ -4919,17 +4842,11 @@ static int gaudi_scrub_device_mem(struct hl_device *hdev, u64 addr, u64 size)
return rc;
}
- mutex_lock(&gaudi->clk_gate_mutex);
- hdev->asic_funcs->disable_clock_gating(hdev);
-
/* Scrub HBM using all DMA channels in parallel */
rc = gaudi_hbm_scrubbing(hdev);
if (rc)
dev_err(hdev->dev,
"Failed to clear HBM in mem scrub all\n");
-
- hdev->asic_funcs->set_clock_gating(hdev);
- mutex_unlock(&gaudi->clk_gate_mutex);
}
return rc;
@@ -6188,7 +6105,6 @@ static int gaudi_debugfs_read32(struct hl_device *hdev, u64 addr,
bool user_address, u32 *val)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct gaudi_device *gaudi = hdev->asic_specific;
u64 hbm_bar_addr, host_phys_end;
int rc = 0;
@@ -6196,38 +6112,31 @@ static int gaudi_debugfs_read32(struct hl_device *hdev, u64 addr,
if ((addr >= CFG_BASE) && (addr < CFG_BASE + CFG_SIZE)) {
- if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) &&
- (hdev->clock_gating_mask &
- GAUDI_CLK_GATE_DEBUGFS_MASK)) {
+ *val = RREG32(addr - CFG_BASE);
- dev_err_ratelimited(hdev->dev,
- "Can't read register - clock gating is enabled!\n");
- rc = -EFAULT;
- } else {
- *val = RREG32(addr - CFG_BASE);
- }
+ } else if ((addr >= SRAM_BASE_ADDR) && (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) {
+
+ *val = readl(hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR));
- } else if ((addr >= SRAM_BASE_ADDR) &&
- (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) {
- *val = readl(hdev->pcie_bar[SRAM_BAR_ID] +
- (addr - SRAM_BASE_ADDR));
} else if (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size) {
- u64 bar_base_addr = DRAM_PHYS_BASE +
- (addr & ~(prop->dram_pci_bar_size - 0x1ull));
+
+ u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull));
hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr);
- if (hbm_bar_addr != U64_MAX) {
- *val = readl(hdev->pcie_bar[HBM_BAR_ID] +
- (addr - bar_base_addr));
- hbm_bar_addr = gaudi_set_hbm_bar_base(hdev,
- hbm_bar_addr);
+ if (hbm_bar_addr != U64_MAX) {
+ *val = readl(hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr));
+ hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr);
}
+
if (hbm_bar_addr == U64_MAX)
rc = -EIO;
+
} else if (addr >= HOST_PHYS_BASE && addr < host_phys_end &&
user_address && !iommu_present(&pci_bus_type)) {
+
*val = *(u32 *) phys_to_virt(addr - HOST_PHYS_BASE);
+
} else {
rc = -EFAULT;
}
@@ -6239,7 +6148,6 @@ static int gaudi_debugfs_write32(struct hl_device *hdev, u64 addr,
bool user_address, u32 val)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct gaudi_device *gaudi = hdev->asic_specific;
u64 hbm_bar_addr, host_phys_end;
int rc = 0;
@@ -6247,38 +6155,31 @@ static int gaudi_debugfs_write32(struct hl_device *hdev, u64 addr,
if ((addr >= CFG_BASE) && (addr < CFG_BASE + CFG_SIZE)) {
- if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) &&
- (hdev->clock_gating_mask &
- GAUDI_CLK_GATE_DEBUGFS_MASK)) {
+ WREG32(addr - CFG_BASE, val);
- dev_err_ratelimited(hdev->dev,
- "Can't write register - clock gating is enabled!\n");
- rc = -EFAULT;
- } else {
- WREG32(addr - CFG_BASE, val);
- }
+ } else if ((addr >= SRAM_BASE_ADDR) && (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) {
+
+ writel(val, hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR));
- } else if ((addr >= SRAM_BASE_ADDR) &&
- (addr < SRAM_BASE_ADDR + SRAM_BAR_SIZE)) {
- writel(val, hdev->pcie_bar[SRAM_BAR_ID] +
- (addr - SRAM_BASE_ADDR));
} else if (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size) {
- u64 bar_base_addr = DRAM_PHYS_BASE +
- (addr & ~(prop->dram_pci_bar_size - 0x1ull));
+
+ u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull));
hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr);
- if (hbm_bar_addr != U64_MAX) {
- writel(val, hdev->pcie_bar[HBM_BAR_ID] +
- (addr - bar_base_addr));
- hbm_bar_addr = gaudi_set_hbm_bar_base(hdev,
- hbm_bar_addr);
+ if (hbm_bar_addr != U64_MAX) {
+ writel(val, hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr));
+ hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr);
}
+
if (hbm_bar_addr == U64_MAX)
rc = -EIO;
+
} else if (addr >= HOST_PHYS_BASE && addr < host_phys_end &&
user_address && !iommu_present(&pci_bus_type)) {
+
*(u32 *) phys_to_virt(addr - HOST_PHYS_BASE) = val;
+
} else {
rc = -EFAULT;
}
@@ -6290,7 +6191,6 @@ static int gaudi_debugfs_read64(struct hl_device *hdev, u64 addr,
bool user_address, u64 *val)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct gaudi_device *gaudi = hdev->asic_specific;
u64 hbm_bar_addr, host_phys_end;
int rc = 0;
@@ -6298,42 +6198,35 @@ static int gaudi_debugfs_read64(struct hl_device *hdev, u64 addr,
if ((addr >= CFG_BASE) && (addr <= CFG_BASE + CFG_SIZE - sizeof(u64))) {
- if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) &&
- (hdev->clock_gating_mask &
- GAUDI_CLK_GATE_DEBUGFS_MASK)) {
-
- dev_err_ratelimited(hdev->dev,
- "Can't read register - clock gating is enabled!\n");
- rc = -EFAULT;
- } else {
- u32 val_l = RREG32(addr - CFG_BASE);
- u32 val_h = RREG32(addr + sizeof(u32) - CFG_BASE);
+ u32 val_l = RREG32(addr - CFG_BASE);
+ u32 val_h = RREG32(addr + sizeof(u32) - CFG_BASE);
- *val = (((u64) val_h) << 32) | val_l;
- }
+ *val = (((u64) val_h) << 32) | val_l;
} else if ((addr >= SRAM_BASE_ADDR) &&
- (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) {
- *val = readq(hdev->pcie_bar[SRAM_BAR_ID] +
- (addr - SRAM_BASE_ADDR));
- } else if (addr <=
- DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) {
- u64 bar_base_addr = DRAM_PHYS_BASE +
- (addr & ~(prop->dram_pci_bar_size - 0x1ull));
+ (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) {
+
+ *val = readq(hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR));
+
+ } else if (addr <= DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) {
+
+ u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull));
hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr);
- if (hbm_bar_addr != U64_MAX) {
- *val = readq(hdev->pcie_bar[HBM_BAR_ID] +
- (addr - bar_base_addr));
- hbm_bar_addr = gaudi_set_hbm_bar_base(hdev,
- hbm_bar_addr);
+ if (hbm_bar_addr != U64_MAX) {
+ *val = readq(hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr));
+ hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr);
}
+
if (hbm_bar_addr == U64_MAX)
rc = -EIO;
+
} else if (addr >= HOST_PHYS_BASE && addr < host_phys_end &&
user_address && !iommu_present(&pci_bus_type)) {
+
*val = *(u64 *) phys_to_virt(addr - HOST_PHYS_BASE);
+
} else {
rc = -EFAULT;
}
@@ -6345,7 +6238,6 @@ static int gaudi_debugfs_write64(struct hl_device *hdev, u64 addr,
bool user_address, u64 val)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct gaudi_device *gaudi = hdev->asic_specific;
u64 hbm_bar_addr, host_phys_end;
int rc = 0;
@@ -6353,41 +6245,33 @@ static int gaudi_debugfs_write64(struct hl_device *hdev, u64 addr,
if ((addr >= CFG_BASE) && (addr <= CFG_BASE + CFG_SIZE - sizeof(u64))) {
- if ((gaudi->hw_cap_initialized & HW_CAP_CLK_GATE) &&
- (hdev->clock_gating_mask &
- GAUDI_CLK_GATE_DEBUGFS_MASK)) {
-
- dev_err_ratelimited(hdev->dev,
- "Can't write register - clock gating is enabled!\n");
- rc = -EFAULT;
- } else {
- WREG32(addr - CFG_BASE, lower_32_bits(val));
- WREG32(addr + sizeof(u32) - CFG_BASE,
- upper_32_bits(val));
- }
+ WREG32(addr - CFG_BASE, lower_32_bits(val));
+ WREG32(addr + sizeof(u32) - CFG_BASE, upper_32_bits(val));
} else if ((addr >= SRAM_BASE_ADDR) &&
- (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) {
- writeq(val, hdev->pcie_bar[SRAM_BAR_ID] +
- (addr - SRAM_BASE_ADDR));
- } else if (addr <=
- DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) {
- u64 bar_base_addr = DRAM_PHYS_BASE +
- (addr & ~(prop->dram_pci_bar_size - 0x1ull));
+ (addr <= SRAM_BASE_ADDR + SRAM_BAR_SIZE - sizeof(u64))) {
+
+ writeq(val, hdev->pcie_bar[SRAM_BAR_ID] + (addr - SRAM_BASE_ADDR));
+
+ } else if (addr <= DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) {
+
+ u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull));
hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, bar_base_addr);
- if (hbm_bar_addr != U64_MAX) {
- writeq(val, hdev->pcie_bar[HBM_BAR_ID] +
- (addr - bar_base_addr));
- hbm_bar_addr = gaudi_set_hbm_bar_base(hdev,
- hbm_bar_addr);
+ if (hbm_bar_addr != U64_MAX) {
+ writeq(val, hdev->pcie_bar[HBM_BAR_ID] + (addr - bar_base_addr));
+ hbm_bar_addr = gaudi_set_hbm_bar_base(hdev, hbm_bar_addr);
}
+
if (hbm_bar_addr == U64_MAX)
rc = -EIO;
+
} else if (addr >= HOST_PHYS_BASE && addr < host_phys_end &&
user_address && !iommu_present(&pci_bus_type)) {
+
*(u64 *) phys_to_virt(addr - HOST_PHYS_BASE) = val;
+
} else {
rc = -EFAULT;
}
@@ -6446,7 +6330,6 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size,
void *blob_addr)
{
u32 dma_core_sts0, err_cause, cfg1, size_left, pos, size_to_dma;
- struct gaudi_device *gaudi = hdev->asic_specific;
u32 qm_glbl_sts0, qm_cgm_sts;
u64 dma_offset, qm_offset;
dma_addr_t dma_addr;
@@ -6462,10 +6345,6 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size,
if (!kernel_addr)
return -ENOMEM;
- mutex_lock(&gaudi->clk_gate_mutex);
-
- hdev->asic_funcs->disable_clock_gating(hdev);
-
hdev->asic_funcs->hw_queues_lock(hdev);
dma_id = gaudi_dma_assignment[GAUDI_PCI_DMA_1];
@@ -6550,10 +6429,6 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size,
out:
hdev->asic_funcs->hw_queues_unlock(hdev);
- hdev->asic_funcs->set_clock_gating(hdev);
-
- mutex_unlock(&gaudi->clk_gate_mutex);
-
hdev->asic_funcs->asic_dma_free_coherent(hdev, SZ_2M, kernel_addr,
dma_addr);
@@ -6601,10 +6476,6 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid)
return;
}
- mutex_lock(&gaudi->clk_gate_mutex);
-
- hdev->asic_funcs->disable_clock_gating(hdev);
-
gaudi_mmu_prepare_reg(hdev, mmDMA0_QM_GLBL_NON_SECURE_PROPS_0, asid);
gaudi_mmu_prepare_reg(hdev, mmDMA0_QM_GLBL_NON_SECURE_PROPS_1, asid);
gaudi_mmu_prepare_reg(hdev, mmDMA0_QM_GLBL_NON_SECURE_PROPS_2, asid);
@@ -6882,10 +6753,6 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid)
gaudi_mmu_prepare_reg(hdev, mmPSOC_GLOBAL_CONF_TRACE_ARUSER, asid);
gaudi_mmu_prepare_reg(hdev, mmPSOC_GLOBAL_CONF_TRACE_AWUSER, asid);
-
- hdev->asic_funcs->set_clock_gating(hdev);
-
- mutex_unlock(&gaudi->clk_gate_mutex);
}
static int gaudi_send_job_on_qman0(struct hl_device *hdev,
@@ -7266,10 +7133,8 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev,
struct ecc_info_extract_params *params, u64 *ecc_address,
u64 *ecc_syndrom, u8 *memory_wrapper_idx)
{
- struct gaudi_device *gaudi = hdev->asic_specific;
u32 i, num_mem_regs, reg, err_bit;
u64 err_addr, err_word = 0;
- int rc = 0;
num_mem_regs = params->num_memories / 32 +
((params->num_memories % 32) ? 1 : 0);
@@ -7282,11 +7147,6 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev,
else
err_addr = params->block_address + GAUDI_ECC_SERR0_OFFSET;
- if (params->disable_clock_gating) {
- mutex_lock(&gaudi->clk_gate_mutex);
- hdev->asic_funcs->disable_clock_gating(hdev);
- }
-
/* Set invalid wrapper index */
*memory_wrapper_idx = 0xFF;
@@ -7303,8 +7163,7 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev,
if (*memory_wrapper_idx == 0xFF) {
dev_err(hdev->dev, "ECC error information cannot be found\n");
- rc = -EINVAL;
- goto enable_clk_gate;
+ return -EINVAL;
}
WREG32(params->block_address + GAUDI_ECC_MEM_SEL_OFFSET,
@@ -7324,14 +7183,7 @@ static int gaudi_extract_ecc_info(struct hl_device *hdev,
WREG32(params->block_address + GAUDI_ECC_MEM_INFO_CLR_OFFSET, reg);
-enable_clk_gate:
- if (params->disable_clock_gating) {
- hdev->asic_funcs->set_clock_gating(hdev);
-
- mutex_unlock(&gaudi->clk_gate_mutex);
- }
-
- return rc;
+ return 0;
}
/*
@@ -7589,7 +7441,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type,
params.block_address = mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET;
params.num_memories = 90;
params.derr = false;
- params.disable_clock_gating = true;
extract_info_from_fw = false;
break;
case GAUDI_EVENT_TPC0_DERR ... GAUDI_EVENT_TPC7_DERR:
@@ -7598,7 +7449,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type,
mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET;
params.num_memories = 90;
params.derr = true;
- params.disable_clock_gating = true;
extract_info_from_fw = false;
break;
case GAUDI_EVENT_MME0_ACC_SERR:
@@ -7609,7 +7459,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type,
params.block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET;
params.num_memories = 128;
params.derr = false;
- params.disable_clock_gating = true;
extract_info_from_fw = false;
break;
case GAUDI_EVENT_MME0_ACC_DERR:
@@ -7620,7 +7469,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type,
params.block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET;
params.num_memories = 128;
params.derr = true;
- params.disable_clock_gating = true;
extract_info_from_fw = false;
break;
case GAUDI_EVENT_MME0_SBAB_SERR:
@@ -7632,7 +7480,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type,
mmMME0_SBAB_BASE + index * MME_ACC_OFFSET;
params.num_memories = 33;
params.derr = false;
- params.disable_clock_gating = true;
extract_info_from_fw = false;
break;
case GAUDI_EVENT_MME0_SBAB_DERR:
@@ -7644,7 +7491,6 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type,
mmMME0_SBAB_BASE + index * MME_ACC_OFFSET;
params.num_memories = 33;
params.derr = true;
- params.disable_clock_gating = true;
extract_info_from_fw = false;
break;
default:
@@ -7819,6 +7665,48 @@ static void gaudi_print_fw_alive_info(struct hl_device *hdev,
fw_alive->thread_id, fw_alive->uptime_seconds);
}
+static void gaudi_print_nic_axi_irq_info(struct hl_device *hdev, u16 event_type,
+ void *data)
+{
+ char desc[64] = "", *type;
+ struct eq_nic_sei_event *eq_nic_sei = data;
+ u16 nic_id = event_type - GAUDI_EVENT_NIC_SEI_0;
+
+ switch (eq_nic_sei->axi_error_cause) {
+ case RXB:
+ type = "RXB";
+ break;
+ case RXE:
+ type = "RXE";
+ break;
+ case TXS:
+ type = "TXS";
+ break;
+ case TXE:
+ type = "TXE";
+ break;
+ case QPC_RESP:
+ type = "QPC_RESP";
+ break;
+ case NON_AXI_ERR:
+ type = "NON_AXI_ERR";
+ break;
+ case TMR:
+ type = "TMR";
+ break;
+ default:
+ dev_err(hdev->dev, "unknown NIC AXI cause %d\n",
+ eq_nic_sei->axi_error_cause);
+ type = "N/A";
+ break;
+ }
+
+ snprintf(desc, sizeof(desc), "NIC%d_%s%d", nic_id, type,
+ eq_nic_sei->id);
+ dev_err_ratelimited(hdev->dev, "Received H/W interrupt %d [\"%s\"]\n",
+ event_type, desc);
+}
+
static int gaudi_non_hard_reset_late_init(struct hl_device *hdev)
{
/* GAUDI doesn't support any reset except hard-reset */
@@ -7966,19 +7854,9 @@ static int gaudi_hbm_event_to_dev(u16 hbm_event_type)
static bool gaudi_tpc_read_interrupts(struct hl_device *hdev, u8 tpc_id,
char *interrupt_name)
{
- struct gaudi_device *gaudi = hdev->asic_specific;
u32 tpc_offset = tpc_id * TPC_CFG_OFFSET, tpc_interrupts_cause, i;
bool soft_reset_required = false;
- /* Accessing the TPC_INTR_CAUSE registers requires disabling the clock
- * gating, and thus cannot be done in CPU-CP and should be done instead
- * by the driver.
- */
-
- mutex_lock(&gaudi->clk_gate_mutex);
-
- hdev->asic_funcs->disable_clock_gating(hdev);
-
tpc_interrupts_cause = RREG32(mmTPC0_CFG_TPC_INTR_CAUSE + tpc_offset) &
TPC0_CFG_TPC_INTR_CAUSE_CAUSE_MASK;
@@ -7996,10 +7874,6 @@ static bool gaudi_tpc_read_interrupts(struct hl_device *hdev, u8 tpc_id,
/* Clear interrupts */
WREG32(mmTPC0_CFG_TPC_INTR_CAUSE + tpc_offset, 0);
- hdev->asic_funcs->set_clock_gating(hdev);
-
- mutex_unlock(&gaudi->clk_gate_mutex);
-
return soft_reset_required;
}
@@ -8066,6 +7940,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev,
struct hl_eq_entry *eq_entry)
{
struct gaudi_device *gaudi = hdev->asic_specific;
+ u64 data = le64_to_cpu(eq_entry->data[0]);
u32 ctl = le32_to_cpu(eq_entry->hdr.ctl);
u32 fw_fatal_err_flag = 0;
u16 event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK)
@@ -8102,6 +7977,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev,
case GAUDI_EVENT_PSOC_MEM_DERR:
case GAUDI_EVENT_PSOC_CORESIGHT_DERR:
case GAUDI_EVENT_SRAM0_DERR ... GAUDI_EVENT_SRAM28_DERR:
+ case GAUDI_EVENT_NIC0_DERR ... GAUDI_EVENT_NIC4_DERR:
case GAUDI_EVENT_DMA_IF0_DERR ... GAUDI_EVENT_DMA_IF3_DERR:
case GAUDI_EVENT_HBM_0_DERR ... GAUDI_EVENT_HBM_3_DERR:
case GAUDI_EVENT_MMU_DERR:
@@ -8202,6 +8078,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev,
case GAUDI_EVENT_PSOC_MEM_SERR:
case GAUDI_EVENT_PSOC_CORESIGHT_SERR:
case GAUDI_EVENT_SRAM0_SERR ... GAUDI_EVENT_SRAM28_SERR:
+ case GAUDI_EVENT_NIC0_SERR ... GAUDI_EVENT_NIC4_SERR:
case GAUDI_EVENT_DMA_IF0_SERR ... GAUDI_EVENT_DMA_IF3_SERR:
case GAUDI_EVENT_HBM_0_SERR ... GAUDI_EVENT_HBM_3_SERR:
fallthrough;
@@ -8263,6 +8140,11 @@ static void gaudi_handle_eqe(struct hl_device *hdev,
hl_fw_unmask_irq(hdev, event_type);
break;
+ case GAUDI_EVENT_NIC_SEI_0 ... GAUDI_EVENT_NIC_SEI_4:
+ gaudi_print_nic_axi_irq_info(hdev, event_type, &data);
+ hl_fw_unmask_irq(hdev, event_type);
+ break;
+
case GAUDI_EVENT_DMA_IF_SEI_0 ... GAUDI_EVENT_DMA_IF_SEI_3:
gaudi_print_irq_info(hdev, event_type, false);
gaudi_print_sm_sei_info(hdev, event_type,
@@ -8274,6 +8156,9 @@ static void gaudi_handle_eqe(struct hl_device *hdev,
hl_fw_unmask_irq(hdev, event_type);
break;
+ case GAUDI_EVENT_STATUS_NIC0_ENG0 ... GAUDI_EVENT_STATUS_NIC4_ENG1:
+ break;
+
case GAUDI_EVENT_FIX_POWER_ENV_S ... GAUDI_EVENT_FIX_THERMAL_ENV_E:
gaudi_print_clk_change_info(hdev, event_type);
hl_fw_unmask_irq(hdev, event_type);
@@ -8314,7 +8199,7 @@ reset_device:
| HL_DRV_RESET_BYPASS_REQ_TO_FW
| fw_fatal_err_flag);
else if (hdev->hard_reset_on_fw_events)
- hl_device_reset(hdev, HL_DRV_RESET_HARD | fw_fatal_err_flag);
+ hl_device_reset(hdev, HL_DRV_RESET_HARD | HL_DRV_RESET_DELAY | fw_fatal_err_flag);
else
hl_fw_unmask_irq(hdev, event_type);
}
@@ -8461,10 +8346,6 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
u64 offset;
int i, dma_id, port;
- mutex_lock(&gaudi->clk_gate_mutex);
-
- hdev->asic_funcs->disable_clock_gating(hdev);
-
if (s)
seq_puts(s,
"\nDMA is_idle QM_GLBL_STS0 QM_CGM_STS DMA_CORE_STS0\n"
@@ -8585,10 +8466,6 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (s)
seq_puts(s, "\n");
- hdev->asic_funcs->set_clock_gating(hdev);
-
- mutex_unlock(&gaudi->clk_gate_mutex);
-
return is_idle;
}
@@ -8628,10 +8505,8 @@ static int gaudi_get_eeprom_data(struct hl_device *hdev, void *data,
* this function should be used only during initialization and/or after reset,
* when there are no active users.
*/
-static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel,
- u32 tpc_id)
+static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel, u32 tpc_id)
{
- struct gaudi_device *gaudi = hdev->asic_specific;
u64 kernel_timeout;
u32 status, offset;
int rc;
@@ -8643,10 +8518,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel,
else
kernel_timeout = HL_DEVICE_TIMEOUT_USEC;
- mutex_lock(&gaudi->clk_gate_mutex);
-
- hdev->asic_funcs->disable_clock_gating(hdev);
-
WREG32(mmTPC0_CFG_QM_KERNEL_BASE_ADDRESS_LOW + offset,
lower_32_bits(tpc_kernel));
WREG32(mmTPC0_CFG_QM_KERNEL_BASE_ADDRESS_HIGH + offset,
@@ -8686,8 +8557,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel,
dev_err(hdev->dev,
"Timeout while waiting for TPC%d icache prefetch\n",
tpc_id);
- hdev->asic_funcs->set_clock_gating(hdev);
- mutex_unlock(&gaudi->clk_gate_mutex);
return -EIO;
}
@@ -8711,8 +8580,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel,
dev_err(hdev->dev,
"Timeout while waiting for TPC%d vector pipe\n",
tpc_id);
- hdev->asic_funcs->set_clock_gating(hdev);
- mutex_unlock(&gaudi->clk_gate_mutex);
return -EIO;
}
@@ -8724,9 +8591,6 @@ static int gaudi_run_tpc_kernel(struct hl_device *hdev, u64 tpc_kernel,
1000,
kernel_timeout);
- hdev->asic_funcs->set_clock_gating(hdev);
- mutex_unlock(&gaudi->clk_gate_mutex);
-
if (rc) {
dev_err(hdev->dev,
"Timeout while waiting for TPC%d kernel to execute\n",
@@ -8791,7 +8655,7 @@ static int gaudi_internal_cb_pool_init(struct hl_device *hdev,
hdev->internal_cb_pool_dma_addr,
HOST_SPACE_INTERNAL_CB_SZ);
- hdev->asic_funcs->mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR);
+ hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR);
mutex_unlock(&ctx->mmu_lock);
if (rc)
@@ -8826,7 +8690,7 @@ static void gaudi_internal_cb_pool_fini(struct hl_device *hdev,
HOST_SPACE_INTERNAL_CB_SZ);
hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base,
HOST_SPACE_INTERNAL_CB_SZ);
- hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
+ hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
mutex_unlock(&ctx->mmu_lock);
gen_pool_destroy(hdev->internal_cb_pool);
@@ -9204,14 +9068,7 @@ static void gaudi_reset_sob(struct hl_device *hdev, void *data)
static void gaudi_set_dma_mask_from_fw(struct hl_device *hdev)
{
- if (RREG32(mmPSOC_GLOBAL_CONF_NON_RST_FLOPS_0) ==
- HL_POWER9_HOST_MAGIC) {
- hdev->power9_64bit_dma_enable = 1;
- hdev->dma_mask = 64;
- } else {
- hdev->power9_64bit_dma_enable = 0;
- hdev->dma_mask = 48;
- }
+ hdev->dma_mask = 48;
}
static u64 gaudi_get_device_time(struct hl_device *hdev)
@@ -9293,23 +9150,15 @@ static int gaudi_gen_sync_to_engine_map(struct hl_device *hdev,
struct hl_sync_to_engine_map *map)
{
struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- struct gaudi_device *gaudi = hdev->asic_specific;
int i, j, rc;
u32 reg_value;
/* Iterate over TPC engines */
for (i = 0; i < sds->props[SP_NUM_OF_TPC_ENGINES]; ++i) {
- /* TPC registered must be accessed with clock gating disabled */
- mutex_lock(&gaudi->clk_gate_mutex);
- hdev->asic_funcs->disable_clock_gating(hdev);
reg_value = RREG32(sds->props[SP_TPC0_CFG_SO] +
sds->props[SP_NEXT_TPC] * i);
- /* We can reenable clock_gating */
- hdev->asic_funcs->set_clock_gating(hdev);
- mutex_unlock(&gaudi->clk_gate_mutex);
-
rc = gaudi_add_sync_to_engine_map_entry(map, reg_value,
ENGINE_TPC, i);
if (rc)
@@ -9319,20 +9168,11 @@ static int gaudi_gen_sync_to_engine_map(struct hl_device *hdev,
/* Iterate over MME engines */
for (i = 0; i < sds->props[SP_NUM_OF_MME_ENGINES]; ++i) {
for (j = 0; j < sds->props[SP_SUB_MME_ENG_NUM]; ++j) {
- /* MME registered must be accessed with clock gating
- * disabled
- */
- mutex_lock(&gaudi->clk_gate_mutex);
- hdev->asic_funcs->disable_clock_gating(hdev);
reg_value = RREG32(sds->props[SP_MME_CFG_SO] +
sds->props[SP_NEXT_MME] * i +
j * sizeof(u32));
- /* We can reenable clock_gating */
- hdev->asic_funcs->set_clock_gating(hdev);
- mutex_unlock(&gaudi->clk_gate_mutex);
-
rc = gaudi_add_sync_to_engine_map_entry(
map, reg_value, ENGINE_MME,
i * sds->props[SP_SUB_MME_ENG_NUM] + j);
@@ -9537,6 +9377,29 @@ static u32 *gaudi_get_stream_master_qid_arr(void)
return gaudi_stream_master;
}
+static ssize_t infineon_ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ struct cpucp_info *cpucp_info;
+
+ cpucp_info = &hdev->asic_prop.cpucp_info;
+
+ return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version));
+}
+
+static DEVICE_ATTR_RO(infineon_ver);
+
+static struct attribute *gaudi_vrm_dev_attrs[] = {
+ &dev_attr_infineon_ver.attr,
+};
+
+static void gaudi_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp,
+ struct attribute_group *dev_vrm_attr_grp)
+{
+ hl_sysfs_add_dev_clk_attr(hdev, dev_clk_attr_grp);
+ dev_vrm_attr_grp->attrs = gaudi_vrm_dev_attrs;
+}
+
static const struct hl_asic_funcs gaudi_funcs = {
.early_init = gaudi_early_init,
.early_fini = gaudi_early_fini,
@@ -9574,17 +9437,14 @@ static const struct hl_asic_funcs gaudi_funcs = {
.debugfs_read64 = gaudi_debugfs_read64,
.debugfs_write64 = gaudi_debugfs_write64,
.debugfs_read_dma = gaudi_debugfs_read_dma,
- .add_device_attr = hl_add_device_attr,
+ .add_device_attr = gaudi_add_device_attr,
.handle_eqe = gaudi_handle_eqe,
- .set_pll_profile = hl_set_pll_profile,
.get_events_stat = gaudi_get_events_stat,
.read_pte = gaudi_read_pte,
.write_pte = gaudi_write_pte,
.mmu_invalidate_cache = gaudi_mmu_invalidate_cache,
.mmu_invalidate_cache_range = gaudi_mmu_invalidate_cache_range,
.send_heartbeat = gaudi_send_heartbeat,
- .set_clock_gating = gaudi_set_clock_gating,
- .disable_clock_gating = gaudi_disable_clock_gating,
.debug_coresight = gaudi_debug_coresight,
.is_device_idle = gaudi_is_device_idle,
.non_hard_reset_late_init = gaudi_non_hard_reset_late_init,
@@ -9600,7 +9460,6 @@ static const struct hl_asic_funcs gaudi_funcs = {
.halt_coresight = gaudi_halt_coresight,
.ctx_init = gaudi_ctx_init,
.ctx_fini = gaudi_ctx_fini,
- .get_clk_rate = hl_get_clk_rate,
.get_queue_id_for_cq = gaudi_get_queue_id_for_cq,
.load_firmware_to_device = gaudi_load_firmware_to_device,
.load_boot_fit_to_device = gaudi_load_boot_fit_to_device,
@@ -9626,7 +9485,8 @@ static const struct hl_asic_funcs gaudi_funcs = {
.state_dump_init = gaudi_state_dump_init,
.get_sob_addr = gaudi_get_sob_addr,
.set_pci_memory_regions = gaudi_set_pci_memory_regions,
- .get_stream_master_qid_arr = gaudi_get_stream_master_qid_arr
+ .get_stream_master_qid_arr = gaudi_get_stream_master_qid_arr,
+ .is_valid_dram_page_size = NULL
};
/**
diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h
index 8ac16a9b7d15..54de7c599072 100644
--- a/drivers/misc/habanalabs/gaudi/gaudiP.h
+++ b/drivers/misc/habanalabs/gaudi/gaudiP.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
*
- * Copyright 2019-2020 HabanaLabs, Ltd.
+ * Copyright 2019-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
@@ -177,7 +177,6 @@
#define HW_CAP_MSI BIT(6)
#define HW_CAP_CPU_Q BIT(7)
#define HW_CAP_HBM_DMA BIT(8)
-#define HW_CAP_CLK_GATE BIT(9)
#define HW_CAP_SRAM_SCRAMBLER BIT(10)
#define HW_CAP_HBM_SCRAMBLER BIT(11)
@@ -313,8 +312,6 @@ struct gaudi_internal_qman_info {
* struct gaudi_device - ASIC specific manage structure.
* @cpucp_info_get: get information on device from CPU-CP
* @hw_queues_lock: protects the H/W queues from concurrent access.
- * @clk_gate_mutex: protects code areas that require clock gating to be disabled
- * temporarily
* @internal_qmans: Internal QMANs information. The array size is larger than
* the actual number of internal queues because they are not in
* consecutive order.
@@ -337,7 +334,6 @@ struct gaudi_device {
/* TODO: remove hw_queues_lock after moving to scheduler code */
spinlock_t hw_queues_lock;
- struct mutex clk_gate_mutex;
struct gaudi_internal_qman_info internal_qmans[GAUDI_QUEUE_ID_SIZE];
@@ -355,8 +351,6 @@ struct gaudi_device {
void gaudi_init_security(struct hl_device *hdev);
void gaudi_ack_protection_bits_errors(struct hl_device *hdev);
-void gaudi_add_device_attr(struct hl_device *hdev,
- struct attribute_group *dev_attr_grp);
int gaudi_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data);
void gaudi_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx);
void gaudi_mmu_prepare_reg(struct hl_device *hdev, u64 reg, u32 asid);
diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c
index fbcc7bbf44b3..ec9358bcbf0b 100644
--- a/drivers/misc/habanalabs/goya/goya.c
+++ b/drivers/misc/habanalabs/goya/goya.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -430,6 +430,9 @@ int goya_set_fixed_properties(struct hl_device *hdev)
prop->dmmu.page_size = PAGE_SIZE_2MB;
prop->dmmu.num_hops = MMU_ARCH_5_HOPS;
prop->dmmu.last_mask = LAST_MASK;
+ /* TODO: will be duplicated until implementing per-MMU props */
+ prop->dmmu.hop_table_size = prop->mmu_hop_table_size;
+ prop->dmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size;
/* shifts and masks are the same in PMMU and DMMU */
memcpy(&prop->pmmu, &prop->dmmu, sizeof(prop->dmmu));
@@ -438,6 +441,9 @@ int goya_set_fixed_properties(struct hl_device *hdev)
prop->pmmu.page_size = PAGE_SIZE_4KB;
prop->pmmu.num_hops = MMU_ARCH_5_HOPS;
prop->pmmu.last_mask = LAST_MASK;
+ /* TODO: will be duplicated until implementing per-MMU props */
+ prop->pmmu.hop_table_size = prop->mmu_hop_table_size;
+ prop->pmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size;
/* PMMU and HPMMU are the same except of page size */
memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu));
@@ -477,6 +483,10 @@ int goya_set_fixed_properties(struct hl_device *hdev)
prop->use_get_power_for_reset_history = true;
+ prop->configurable_stop_on_err = true;
+
+ prop->set_max_power_on_device_init = true;
+
return 0;
}
@@ -893,7 +903,7 @@ int goya_late_init(struct hl_device *hdev)
goya->pm_mng_profile = PM_AUTO;
- hdev->asic_funcs->set_pll_profile(hdev, PLL_LOW);
+ goya_set_pll_profile(hdev, PLL_LOW);
schedule_delayed_work(&goya->goya_work->work_freq,
usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC));
@@ -2700,8 +2710,7 @@ int goya_mmu_init(struct hl_device *hdev)
WREG32_AND(mmSTLB_STLB_FEATURE_EN,
(~STLB_STLB_FEATURE_EN_FOLLOWER_EN_MASK));
- hdev->asic_funcs->mmu_invalidate_cache(hdev, true,
- MMU_OP_USERPTR | MMU_OP_PHYS_PACK);
+ hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR | MMU_OP_PHYS_PACK);
WREG32(mmMMU_MMU_ENABLE, 1);
WREG32(mmMMU_SPI_MASK, 0xF);
@@ -5341,7 +5350,7 @@ static int goya_mmu_invalidate_cache_range(struct hl_device *hdev,
/* Treat as invalidate all because there is no range invalidation
* in Goya
*/
- return hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags);
+ return hl_mmu_invalidate_cache(hdev, is_hard, flags);
}
int goya_send_heartbeat(struct hl_device *hdev)
@@ -5391,16 +5400,6 @@ int goya_cpucp_info_get(struct hl_device *hdev)
return 0;
}
-static void goya_set_clock_gating(struct hl_device *hdev)
-{
- /* clock gating not supported in Goya */
-}
-
-static void goya_disable_clock_gating(struct hl_device *hdev)
-{
- /* clock gating not supported in Goya */
-}
-
static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
u8 mask_len, struct seq_file *s)
{
@@ -5564,16 +5563,7 @@ static void goya_reset_sob_group(struct hl_device *hdev, u16 sob_group)
static void goya_set_dma_mask_from_fw(struct hl_device *hdev)
{
- if (RREG32(mmPSOC_GLOBAL_CONF_NON_RST_FLOPS_0) ==
- HL_POWER9_HOST_MAGIC) {
- dev_dbg(hdev->dev, "Working in 64-bit DMA mode\n");
- hdev->power9_64bit_dma_enable = 1;
- hdev->dma_mask = 64;
- } else {
- dev_dbg(hdev->dev, "Working in 48-bit DMA mode\n");
- hdev->power9_64bit_dma_enable = 0;
- hdev->dma_mask = 48;
- }
+ hdev->dma_mask = 48;
}
u64 goya_get_device_time(struct hl_device *hdev)
@@ -5727,15 +5717,12 @@ static const struct hl_asic_funcs goya_funcs = {
.debugfs_read_dma = goya_debugfs_read_dma,
.add_device_attr = goya_add_device_attr,
.handle_eqe = goya_handle_eqe,
- .set_pll_profile = goya_set_pll_profile,
.get_events_stat = goya_get_events_stat,
.read_pte = goya_read_pte,
.write_pte = goya_write_pte,
.mmu_invalidate_cache = goya_mmu_invalidate_cache,
.mmu_invalidate_cache_range = goya_mmu_invalidate_cache_range,
.send_heartbeat = goya_send_heartbeat,
- .set_clock_gating = goya_set_clock_gating,
- .disable_clock_gating = goya_disable_clock_gating,
.debug_coresight = goya_debug_coresight,
.is_device_idle = goya_is_device_idle,
.non_hard_reset_late_init = goya_non_hard_reset_late_init,
@@ -5751,7 +5738,6 @@ static const struct hl_asic_funcs goya_funcs = {
.halt_coresight = goya_halt_coresight,
.ctx_init = goya_ctx_init,
.ctx_fini = goya_ctx_fini,
- .get_clk_rate = hl_get_clk_rate,
.get_queue_id_for_cq = goya_get_queue_id_for_cq,
.load_firmware_to_device = goya_load_firmware_to_device,
.load_boot_fit_to_device = goya_load_boot_fit_to_device,
@@ -5778,6 +5764,7 @@ static const struct hl_asic_funcs goya_funcs = {
.get_sob_addr = &goya_get_sob_addr,
.set_pci_memory_regions = goya_set_pci_memory_regions,
.get_stream_master_qid_arr = goya_get_stream_master_qid_arr,
+ .is_valid_dram_page_size = NULL
};
/*
diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h
index 3740fd25bf84..647f57402616 100644
--- a/drivers/misc/habanalabs/goya/goyaP.h
+++ b/drivers/misc/habanalabs/goya/goyaP.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
*
- * Copyright 2016-2019 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
@@ -217,8 +217,8 @@ u64 goya_get_max_power(struct hl_device *hdev);
void goya_set_max_power(struct hl_device *hdev, u64 value);
void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq);
-void goya_add_device_attr(struct hl_device *hdev,
- struct attribute_group *dev_attr_grp);
+void goya_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp,
+ struct attribute_group *dev_vrm_attr_grp);
int goya_cpucp_info_get(struct hl_device *hdev);
int goya_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data);
void goya_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx);
diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/misc/habanalabs/goya/goya_hwmgr.c
index 76b47749affe..6580fc6a486a 100644
--- a/drivers/misc/habanalabs/goya/goya_hwmgr.c
+++ b/drivers/misc/habanalabs/goya/goya_hwmgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
+ * Copyright 2016-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*/
@@ -11,21 +11,24 @@ void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq)
{
struct goya_device *goya = hdev->asic_specific;
+ if (!hdev->pdev)
+ return;
+
switch (freq) {
case PLL_HIGH:
- hl_set_frequency(hdev, HL_GOYA_MME_PLL, hdev->high_pll);
- hl_set_frequency(hdev, HL_GOYA_TPC_PLL, hdev->high_pll);
- hl_set_frequency(hdev, HL_GOYA_IC_PLL, hdev->high_pll);
+ hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, hdev->high_pll);
+ hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, hdev->high_pll);
+ hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, hdev->high_pll);
break;
case PLL_LOW:
- hl_set_frequency(hdev, HL_GOYA_MME_PLL, GOYA_PLL_FREQ_LOW);
- hl_set_frequency(hdev, HL_GOYA_TPC_PLL, GOYA_PLL_FREQ_LOW);
- hl_set_frequency(hdev, HL_GOYA_IC_PLL, GOYA_PLL_FREQ_LOW);
+ hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, GOYA_PLL_FREQ_LOW);
+ hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, GOYA_PLL_FREQ_LOW);
+ hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, GOYA_PLL_FREQ_LOW);
break;
case PLL_LAST:
- hl_set_frequency(hdev, HL_GOYA_MME_PLL, goya->mme_clk);
- hl_set_frequency(hdev, HL_GOYA_TPC_PLL, goya->tpc_clk);
- hl_set_frequency(hdev, HL_GOYA_IC_PLL, goya->ic_clk);
+ hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, goya->mme_clk);
+ hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, goya->tpc_clk);
+ hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, goya->ic_clk);
break;
default:
dev_err(hdev->dev, "unknown frequency setting\n");
@@ -41,7 +44,7 @@ static ssize_t mme_clk_show(struct device *dev, struct device_attribute *attr,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- value = hl_get_frequency(hdev, HL_GOYA_MME_PLL, false);
+ value = hl_fw_get_frequency(hdev, HL_GOYA_MME_PLL, false);
if (value < 0)
return value;
@@ -74,7 +77,7 @@ static ssize_t mme_clk_store(struct device *dev, struct device_attribute *attr,
goto fail;
}
- hl_set_frequency(hdev, HL_GOYA_MME_PLL, value);
+ hl_fw_set_frequency(hdev, HL_GOYA_MME_PLL, value);
goya->mme_clk = value;
fail:
@@ -90,7 +93,7 @@ static ssize_t tpc_clk_show(struct device *dev, struct device_attribute *attr,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- value = hl_get_frequency(hdev, HL_GOYA_TPC_PLL, false);
+ value = hl_fw_get_frequency(hdev, HL_GOYA_TPC_PLL, false);
if (value < 0)
return value;
@@ -123,7 +126,7 @@ static ssize_t tpc_clk_store(struct device *dev, struct device_attribute *attr,
goto fail;
}
- hl_set_frequency(hdev, HL_GOYA_TPC_PLL, value);
+ hl_fw_set_frequency(hdev, HL_GOYA_TPC_PLL, value);
goya->tpc_clk = value;
fail:
@@ -139,7 +142,7 @@ static ssize_t ic_clk_show(struct device *dev, struct device_attribute *attr,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- value = hl_get_frequency(hdev, HL_GOYA_IC_PLL, false);
+ value = hl_fw_get_frequency(hdev, HL_GOYA_IC_PLL, false);
if (value < 0)
return value;
@@ -172,7 +175,7 @@ static ssize_t ic_clk_store(struct device *dev, struct device_attribute *attr,
goto fail;
}
- hl_set_frequency(hdev, HL_GOYA_IC_PLL, value);
+ hl_fw_set_frequency(hdev, HL_GOYA_IC_PLL, value);
goya->ic_clk = value;
fail:
@@ -188,7 +191,7 @@ static ssize_t mme_clk_curr_show(struct device *dev,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- value = hl_get_frequency(hdev, HL_GOYA_MME_PLL, true);
+ value = hl_fw_get_frequency(hdev, HL_GOYA_MME_PLL, true);
if (value < 0)
return value;
@@ -205,7 +208,7 @@ static ssize_t tpc_clk_curr_show(struct device *dev,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- value = hl_get_frequency(hdev, HL_GOYA_TPC_PLL, true);
+ value = hl_fw_get_frequency(hdev, HL_GOYA_TPC_PLL, true);
if (value < 0)
return value;
@@ -222,7 +225,7 @@ static ssize_t ic_clk_curr_show(struct device *dev,
if (!hl_device_operational(hdev, NULL))
return -ENODEV;
- value = hl_get_frequency(hdev, HL_GOYA_IC_PLL, true);
+ value = hl_fw_get_frequency(hdev, HL_GOYA_IC_PLL, true);
if (value < 0)
return value;
@@ -347,7 +350,7 @@ static DEVICE_ATTR_RW(pm_mng_profile);
static DEVICE_ATTR_RW(tpc_clk);
static DEVICE_ATTR_RO(tpc_clk_curr);
-static struct attribute *goya_dev_attrs[] = {
+static struct attribute *goya_clk_dev_attrs[] = {
&dev_attr_high_pll.attr,
&dev_attr_ic_clk.attr,
&dev_attr_ic_clk_curr.attr,
@@ -356,11 +359,27 @@ static struct attribute *goya_dev_attrs[] = {
&dev_attr_pm_mng_profile.attr,
&dev_attr_tpc_clk.attr,
&dev_attr_tpc_clk_curr.attr,
- NULL,
};
-void goya_add_device_attr(struct hl_device *hdev,
- struct attribute_group *dev_attr_grp)
+static ssize_t infineon_ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ struct cpucp_info *cpucp_info;
+
+ cpucp_info = &hdev->asic_prop.cpucp_info;
+
+ return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version));
+}
+
+static DEVICE_ATTR_RO(infineon_ver);
+
+static struct attribute *goya_vrm_dev_attrs[] = {
+ &dev_attr_infineon_ver.attr,
+};
+
+void goya_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp,
+ struct attribute_group *dev_vrm_attr_grp)
{
- dev_attr_grp->attrs = goya_dev_attrs;
+ dev_clk_attr_grp->attrs = goya_clk_dev_attrs;
+ dev_vrm_attr_grp->attrs = goya_vrm_dev_attrs;
}
diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h
index f9c4acc9bf5a..65668dac6a5f 100644
--- a/drivers/misc/habanalabs/include/common/cpucp_if.h
+++ b/drivers/misc/habanalabs/include/common/cpucp_if.h
@@ -780,6 +780,7 @@ struct cpucp_security_info {
* (0 = functional 1 = binned)
* @xbar_binning_mask: Xbar binning mask, 1 bit per Xbar instance
* (0 = functional 1 = binned)
+ * @fw_os_version: Firmware OS Version
*/
struct cpucp_info {
struct cpucp_sensor sensors[CPUCP_MAX_SENSORS];
@@ -807,6 +808,7 @@ struct cpucp_info {
__le32 reserved6;
__u8 pll_map[PLL_MAP_LEN];
__le64 mme_binning_mask;
+ __u8 fw_os_version[VERSION_MAX_LEN];
};
struct cpucp_mac_addr {
diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h
index 135e21d6edc9..15f91ae9de6e 100644
--- a/drivers/misc/habanalabs/include/common/hl_boot_if.h
+++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h
@@ -33,6 +33,7 @@ enum cpu_boot_err {
CPU_BOOT_ERR_BOOT_FW_CRIT_ERR = 18,
CPU_BOOT_ERR_BINNING_FAIL = 19,
CPU_BOOT_ERR_TPM_FAIL = 20,
+ CPU_BOOT_ERR_TMP_THRESH_INIT_FAIL = 21,
CPU_BOOT_ERR_ENABLED = 31,
CPU_BOOT_ERR_SCND_EN = 63,
CPU_BOOT_ERR_LAST = 64 /* we have 2 registers of 32 bits */
@@ -111,6 +112,9 @@ enum cpu_boot_err {
*
* CPU_BOOT_ERR0_TPM_FAIL TPM verification flow failed.
*
+ * CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL Failed to set threshold for tmperature
+ * sensor.
+ *
* CPU_BOOT_ERR0_ENABLED Error registers enabled.
* This is a main indication that the
* running FW populates the error
@@ -134,6 +138,7 @@ enum cpu_boot_err {
#define CPU_BOOT_ERR0_BOOT_FW_CRIT_ERR (1 << CPU_BOOT_ERR_BOOT_FW_CRIT_ERR)
#define CPU_BOOT_ERR0_BINNING_FAIL (1 << CPU_BOOT_ERR_BINNING_FAIL)
#define CPU_BOOT_ERR0_TPM_FAIL (1 << CPU_BOOT_ERR_TPM_FAIL)
+#define CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL (1 << CPU_BOOT_ERR_TMP_THRESH_INIT_FAIL)
#define CPU_BOOT_ERR0_ENABLED (1 << CPU_BOOT_ERR_ENABLED)
#define CPU_BOOT_ERR1_ENABLED (1 << CPU_BOOT_ERR_ENABLED)
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h b/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h
index d966bd4dfea6..c07ed4ed304c 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h
+++ b/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h
@@ -311,6 +311,16 @@ enum gaudi_async_event_id {
GAUDI_EVENT_FW_ALIVE_S = 645,
GAUDI_EVENT_DEV_RESET_REQ = 646,
GAUDI_EVENT_PKT_QUEUE_OUT_SYNC = 647,
+ GAUDI_EVENT_STATUS_NIC0_ENG0 = 648,
+ GAUDI_EVENT_STATUS_NIC0_ENG1 = 649,
+ GAUDI_EVENT_STATUS_NIC1_ENG0 = 650,
+ GAUDI_EVENT_STATUS_NIC1_ENG1 = 651,
+ GAUDI_EVENT_STATUS_NIC2_ENG0 = 652,
+ GAUDI_EVENT_STATUS_NIC2_ENG1 = 653,
+ GAUDI_EVENT_STATUS_NIC3_ENG0 = 654,
+ GAUDI_EVENT_STATUS_NIC3_ENG1 = 655,
+ GAUDI_EVENT_STATUS_NIC4_ENG0 = 656,
+ GAUDI_EVENT_STATUS_NIC4_ENG1 = 657,
GAUDI_EVENT_FIX_POWER_ENV_S = 658,
GAUDI_EVENT_FIX_POWER_ENV_E = 659,
GAUDI_EVENT_FIX_THERMAL_ENV_S = 660,
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 67c5b452dd35..88b91ad8e541 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -1070,10 +1070,10 @@ static int kgdbts_option_setup(char *opt)
{
if (strlen(opt) >= MAX_CONFIG_LEN) {
printk(KERN_ERR "kgdbts: config string too long\n");
- return -ENOSPC;
+ return 1;
}
strcpy(config, opt);
- return 0;
+ return 1;
}
__setup("kgdbts=", kgdbts_option_setup);
diff --git a/drivers/misc/lkdtm/fortify.c b/drivers/misc/lkdtm/fortify.c
index d06458a4858e..ab33bb5e2e7a 100644
--- a/drivers/misc/lkdtm/fortify.c
+++ b/drivers/misc/lkdtm/fortify.c
@@ -44,14 +44,14 @@ void lkdtm_FORTIFIED_SUBOBJECT(void)
strscpy(src, "over ten bytes", size);
size = strlen(src) + 1;
- pr_info("trying to strcpy past the end of a member of a struct\n");
+ pr_info("trying to strncpy past the end of a member of a struct\n");
/*
- * memcpy(target.a, src, 20); will hit a compile error because the
+ * strncpy(target.a, src, 20); will hit a compile error because the
* compiler knows at build time that target.a < 20 bytes. Use a
* volatile to force a runtime error.
*/
- memcpy(target.a, src, size);
+ strncpy(target.a, src, size);
/* Store result to global to prevent the code from being eliminated */
fortify_scratch_space = target.a[3];
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 06734670a732..31264ab2eb13 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -2148,6 +2148,7 @@ void mei_cl_all_disconnect(struct mei_device *dev)
list_for_each_entry(cl, &dev->file_list, link)
mei_cl_set_disconnected(cl);
}
+EXPORT_SYMBOL_GPL(mei_cl_all_disconnect);
static struct mei_cl *mei_cl_dma_map_find(struct mei_device *dev, u8 buffer_id)
{
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index 67bb6a25fd0a..64ce3f830262 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -107,6 +107,7 @@
#define MEI_DEV_ID_ADP_S 0x7AE8 /* Alder Lake Point S */
#define MEI_DEV_ID_ADP_LP 0x7A60 /* Alder Lake Point LP */
#define MEI_DEV_ID_ADP_P 0x51E0 /* Alder Lake Point P */
+#define MEI_DEV_ID_ADP_N 0x54E0 /* Alder Lake Point N */
/*
* MEI HW Section
@@ -120,6 +121,7 @@
#define PCI_CFG_HFS_2 0x48
#define PCI_CFG_HFS_3 0x60
# define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070
+# define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000
# define PCI_CFG_HFS_3_FW_SKU_SPS 0x00000060
#define PCI_CFG_HFS_4 0x64
#define PCI_CFG_HFS_5 0x68
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index d3a6c0728645..719fee9af156 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -1257,7 +1257,11 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/* check if ME wants a reset */
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
dev_warn(dev->dev, "FW not ready: resetting.\n");
- schedule_work(&dev->reset_work);
+ if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
+ dev->dev_state == MEI_DEV_POWER_DOWN)
+ mei_cl_all_disconnect(dev);
+ else if (dev->dev_state != MEI_DEV_DISABLED)
+ schedule_work(&dev->reset_work);
goto end;
}
@@ -1289,12 +1293,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
if (rets == -ENODATA)
break;
- if (rets &&
- (dev->dev_state != MEI_DEV_RESETTING &&
- dev->dev_state != MEI_DEV_POWER_DOWN)) {
- dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n",
- rets);
- schedule_work(&dev->reset_work);
+ if (rets) {
+ dev_err(dev->dev, "mei_irq_read_handler ret = %d, state = %d.\n",
+ rets, dev->dev_state);
+ if (dev->dev_state != MEI_DEV_RESETTING &&
+ dev->dev_state != MEI_DEV_DISABLED &&
+ dev->dev_state != MEI_DEV_POWERING_DOWN &&
+ dev->dev_state != MEI_DEV_POWER_DOWN)
+ schedule_work(&dev->reset_work);
goto end;
}
}
@@ -1405,16 +1411,16 @@ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev)
.quirk_probe = mei_me_fw_type_sps_4
/**
- * mei_me_fw_type_sps() - check for sps sku
+ * mei_me_fw_type_sps_ign() - check for sps or ign sku
*
- * Read ME FW Status register to check for SPS Firmware.
- * The SPS FW is only signaled in pci function 0
+ * Read ME FW Status register to check for SPS or IGN Firmware.
+ * The SPS/IGN FW is only signaled in pci function 0
*
* @pdev: pci device
*
- * Return: true in case of SPS firmware
+ * Return: true in case of SPS/IGN firmware
*/
-static bool mei_me_fw_type_sps(const struct pci_dev *pdev)
+static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev)
{
u32 reg;
u32 fw_type;
@@ -1427,14 +1433,15 @@ static bool mei_me_fw_type_sps(const struct pci_dev *pdev)
dev_dbg(&pdev->dev, "fw type is %d\n", fw_type);
- return fw_type == PCI_CFG_HFS_3_FW_SKU_SPS;
+ return fw_type == PCI_CFG_HFS_3_FW_SKU_IGN ||
+ fw_type == PCI_CFG_HFS_3_FW_SKU_SPS;
}
#define MEI_CFG_KIND_ITOUCH \
.kind = "itouch"
-#define MEI_CFG_FW_SPS \
- .quirk_probe = mei_me_fw_type_sps
+#define MEI_CFG_FW_SPS_IGN \
+ .quirk_probe = mei_me_fw_type_sps_ign
#define MEI_CFG_FW_VER_SUPP \
.fw_ver_supported = 1
@@ -1535,7 +1542,7 @@ static const struct mei_cfg mei_me_pch12_sps_cfg = {
MEI_CFG_PCH8_HFS,
MEI_CFG_FW_VER_SUPP,
MEI_CFG_DMA_128,
- MEI_CFG_FW_SPS,
+ MEI_CFG_FW_SPS_IGN,
};
/* Cannon Lake itouch with quirk for SPS 5.0 and newer Firmware exclusion
@@ -1545,7 +1552,7 @@ static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = {
MEI_CFG_KIND_ITOUCH,
MEI_CFG_PCH8_HFS,
MEI_CFG_FW_VER_SUPP,
- MEI_CFG_FW_SPS,
+ MEI_CFG_FW_SPS_IGN,
};
/* Tiger Lake and newer devices */
@@ -1562,7 +1569,7 @@ static const struct mei_cfg mei_me_pch15_sps_cfg = {
MEI_CFG_FW_VER_SUPP,
MEI_CFG_DMA_128,
MEI_CFG_TRC,
- MEI_CFG_FW_SPS,
+ MEI_CFG_FW_SPS_IGN,
};
/*
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index f79076c67256..eb052005ca86 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -161,6 +161,11 @@ int mei_reset(struct mei_device *dev)
return ret;
}
+ if (dev->dev_state != MEI_DEV_RESETTING) {
+ dev_dbg(dev->dev, "wrong state = %d on link start\n", dev->dev_state);
+ return 0;
+ }
+
dev_dbg(dev->dev, "link is established start sending messages.\n");
mei_set_devstate(dev, MEI_DEV_INIT_CLIENTS);
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index a67f4f2d33a9..0706322154cb 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -424,31 +424,26 @@ int mei_irq_read_handler(struct mei_device *dev,
list_for_each_entry(cl, &dev->file_list, link) {
if (mei_cl_hbm_equal(cl, mei_hdr)) {
cl_dbg(dev, cl, "got a message\n");
- break;
+ ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list);
+ goto reset_slots;
}
}
/* if no recipient cl was found we assume corrupted header */
- if (&cl->link == &dev->file_list) {
- /* A message for not connected fixed address clients
- * should be silently discarded
- * On power down client may be force cleaned,
- * silently discard such messages
- */
- if (hdr_is_fixed(mei_hdr) ||
- dev->dev_state == MEI_DEV_POWER_DOWN) {
- mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length);
- ret = 0;
- goto reset_slots;
- }
- dev_err(dev->dev, "no destination client found 0x%08X\n",
- dev->rd_msg_hdr[0]);
- ret = -EBADMSG;
- goto end;
+ /* A message for not connected fixed address clients
+ * should be silently discarded
+ * On power down client may be force cleaned,
+ * silently discard such messages
+ */
+ if (hdr_is_fixed(mei_hdr) ||
+ dev->dev_state == MEI_DEV_POWER_DOWN) {
+ mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length);
+ ret = 0;
+ goto reset_slots;
}
-
- ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list);
-
+ dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr[0]);
+ ret = -EBADMSG;
+ goto end;
reset_slots:
/* reset the number of slots and header */
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 3a45aaf002ac..33e58821e478 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/dma-mapping.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -113,6 +114,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_ADP_S, MEI_ME_PCH15_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ADP_LP, MEI_ME_PCH15_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ADP_P, MEI_ME_PCH15_CFG)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_N, MEI_ME_PCH15_CFG)},
/* required last entry */
{0, }
@@ -192,14 +194,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto end;
}
- if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) ||
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) {
-
- err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (err)
- err = dma_set_coherent_mask(&pdev->dev,
- DMA_BIT_MASK(32));
- }
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
dev_err(&pdev->dev, "No usable DMA configuration, aborting\n");
goto end;
diff --git a/drivers/misc/ocxl/link.c b/drivers/misc/ocxl/link.c
index ab039c115381..9670d02c927f 100644
--- a/drivers/misc/ocxl/link.c
+++ b/drivers/misc/ocxl/link.c
@@ -94,7 +94,7 @@ struct ocxl_link {
struct spa *spa;
void *platform_data;
};
-static struct list_head links_list = LIST_HEAD_INIT(links_list);
+static LIST_HEAD(links_list);
static DEFINE_MUTEX(links_list_lock);
enum xsl_response {
diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c
new file mode 100644
index 000000000000..c61be3404c6f
--- /dev/null
+++ b/drivers/misc/open-dice.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 - Google LLC
+ * Author: David Brazdil <dbrazdil@google.com>
+ *
+ * Driver for Open Profile for DICE.
+ *
+ * This driver takes ownership of a reserved memory region containing data
+ * generated by the Open Profile for DICE measured boot protocol. The memory
+ * contents are not interpreted by the kernel but can be mapped into a userspace
+ * process via a misc device. Userspace can also request a wipe of the memory.
+ *
+ * Userspace can access the data with (w/o error handling):
+ *
+ * fd = open("/dev/open-dice0", O_RDWR);
+ * read(fd, &size, sizeof(unsigned long));
+ * data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ * write(fd, NULL, 0); // wipe
+ * close(fd);
+ */
+
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "open-dice"
+
+struct open_dice_drvdata {
+ struct mutex lock;
+ char name[16];
+ struct reserved_mem *rmem;
+ struct miscdevice misc;
+};
+
+static inline struct open_dice_drvdata *to_open_dice_drvdata(struct file *filp)
+{
+ return container_of(filp->private_data, struct open_dice_drvdata, misc);
+}
+
+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);
+ if (IS_ERR(kaddr)) {
+ mutex_unlock(&drvdata->lock);
+ return PTR_ERR(kaddr);
+ }
+
+ memset(kaddr, 0, drvdata->rmem->size);
+ devm_memunmap(drvdata->misc.this_device, kaddr);
+ mutex_unlock(&drvdata->lock);
+ return 0;
+}
+
+/*
+ * Copies the size of the reserved memory region to the user-provided buffer.
+ */
+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;
+
+ return simple_read_from_buffer(ptr, len, off, &val, sizeof(val));
+}
+
+/*
+ * Triggers a wipe of the reserved memory region. The user-provided pointer
+ * is never dereferenced.
+ */
+static ssize_t open_dice_write(struct file *filp, const char __user *ptr,
+ size_t len, loff_t *off)
+{
+ if (open_dice_wipe(to_open_dice_drvdata(filp)))
+ return -EIO;
+
+ /* Consume the input buffer. */
+ return len;
+}
+
+/*
+ * Creates a mapping of the reserved memory region in user address space.
+ */
+static int open_dice_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct open_dice_drvdata *drvdata = to_open_dice_drvdata(filp);
+
+ /* Do not allow userspace to modify the underlying data. */
+ if ((vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_SHARED))
+ return -EPERM;
+
+ /* Ensure userspace cannot acquire VM_WRITE + VM_SHARED later. */
+ if (vma->vm_flags & VM_WRITE)
+ vma->vm_flags &= ~VM_MAYSHARE;
+ else if (vma->vm_flags & VM_SHARED)
+ vma->vm_flags &= ~VM_MAYWRITE;
+
+ /* Create write-combine mapping so all clients observe a wipe. */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_flags |= VM_DONTCOPY | VM_DONTDUMP;
+ return vm_iomap_memory(vma, drvdata->rmem->base, drvdata->rmem->size);
+}
+
+static const struct file_operations open_dice_fops = {
+ .owner = THIS_MODULE,
+ .read = open_dice_read,
+ .write = open_dice_write,
+ .mmap = open_dice_mmap,
+};
+
+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;
+ int ret;
+
+ rmem = of_reserved_mem_lookup(dev->of_node);
+ if (!rmem) {
+ dev_err(dev, "failed to lookup reserved memory\n");
+ return -EINVAL;
+ }
+
+ if (!rmem->size || (rmem->size > ULONG_MAX)) {
+ dev_err(dev, "invalid memory region size\n");
+ return -EINVAL;
+ }
+
+ if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) {
+ dev_err(dev, "memory region must be page-aligned\n");
+ return -EINVAL;
+ }
+
+ drvdata = devm_kmalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ *drvdata = (struct open_dice_drvdata){
+ .lock = __MUTEX_INITIALIZER(drvdata->lock),
+ .rmem = rmem,
+ .misc = (struct miscdevice){
+ .parent = dev,
+ .name = drvdata->name,
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &open_dice_fops,
+ .mode = 0600,
+ },
+ };
+
+ /* Index overflow check not needed, misc_register() will fail. */
+ snprintf(drvdata->name, sizeof(drvdata->name), DRIVER_NAME"%u", dev_idx++);
+
+ ret = misc_register(&drvdata->misc);
+ if (ret) {
+ dev_err(dev, "failed to register misc device '%s': %d\n",
+ drvdata->name, ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+ return 0;
+}
+
+static int open_dice_remove(struct platform_device *pdev)
+{
+ struct open_dice_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ misc_deregister(&drvdata->misc);
+ return 0;
+}
+
+static const struct of_device_id open_dice_of_match[] = {
+ { .compatible = "google,open-dice" },
+ {},
+};
+
+static struct platform_driver open_dice_driver = {
+ .remove = open_dice_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = open_dice_of_match,
+ },
+};
+
+static int __init open_dice_init(void)
+{
+ int ret = platform_driver_probe(&open_dice_driver, open_dice_probe);
+
+ /* DICE regions are optional. Succeed even with zero instances. */
+ return (ret == -ENODEV) ? 0 : ret;
+}
+
+static void __exit open_dice_exit(void)
+{
+ platform_driver_unregister(&open_dice_driver);
+}
+
+module_init(open_dice_init);
+module_exit(open_dice_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c
index 0ea923fe6371..19dbdad8ad8a 100644
--- a/drivers/misc/sgi-gru/grukservices.c
+++ b/drivers/misc/sgi-gru/grukservices.c
@@ -1016,7 +1016,7 @@ static int quicktest1(unsigned long arg)
break;
}
if (ret != MQE_QUEUE_FULL || i != 4) {
- printk(KERN_DEBUG "GRU:%d quicktest1: unexpect status %d, i %d\n",
+ printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n",
smp_processor_id(), ret, i);
goto done;
}
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h
index e4c067c61251..5efc869fe59a 100644
--- a/drivers/misc/sgi-gru/grutables.h
+++ b/drivers/misc/sgi-gru/grutables.h
@@ -530,12 +530,6 @@ struct gru_blade_state {
for ((i) = (k)*GRU_CBR_AU_SIZE; \
(i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++)
-/* Scan each DSR in a DSR bitmap. Note: multiple DSRs in an allocation unit */
-#define for_each_dsr_in_allocation_map(i, map, k) \
- for_each_set_bit((k), (const unsigned long *)(map), GRU_DSR_AU) \
- for ((i) = (k) * GRU_DSR_AU_CL; \
- (i) < ((k) + 1) * GRU_DSR_AU_CL; (i)++)
-
#define gseg_physical_address(gru, ctxnum) \
((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE)
#define gseg_virtual_address(gru, ctxnum) \
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c
index 1018dc77269d..57a6157209a1 100644
--- a/drivers/misc/vmw_vmci/vmci_guest.c
+++ b/drivers/misc/vmw_vmci/vmci_guest.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/processor.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -31,6 +32,12 @@
#define VMCI_UTIL_NUM_RESOURCES 1
+/*
+ * Datagram buffers for DMA send/receive must accommodate at least
+ * a maximum sized datagram and the header.
+ */
+#define VMCI_DMA_DG_BUFFER_SIZE (VMCI_MAX_DG_SIZE + PAGE_SIZE)
+
static bool vmci_disable_msi;
module_param_named(disable_msi, vmci_disable_msi, bool, 0);
MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
@@ -45,13 +52,18 @@ static u32 vm_context_id = VMCI_INVALID_ID;
struct vmci_guest_device {
struct device *dev; /* PCI device we are attached to */
void __iomem *iobase;
+ void __iomem *mmio_base;
bool exclusive_vectors;
struct tasklet_struct datagram_tasklet;
struct tasklet_struct bm_tasklet;
+ struct wait_queue_head inout_wq;
void *data_buffer;
+ dma_addr_t data_buffer_base;
+ void *tx_buffer;
+ dma_addr_t tx_buffer_base;
void *notification_bitmap;
dma_addr_t notification_base;
};
@@ -89,6 +101,92 @@ u32 vmci_get_vm_context_id(void)
return vm_context_id;
}
+static unsigned int vmci_read_reg(struct vmci_guest_device *dev, u32 reg)
+{
+ if (dev->mmio_base != NULL)
+ return readl(dev->mmio_base + reg);
+ return ioread32(dev->iobase + reg);
+}
+
+static void vmci_write_reg(struct vmci_guest_device *dev, u32 val, u32 reg)
+{
+ if (dev->mmio_base != NULL)
+ writel(val, dev->mmio_base + reg);
+ else
+ iowrite32(val, dev->iobase + reg);
+}
+
+static void vmci_read_data(struct vmci_guest_device *vmci_dev,
+ void *dest, size_t size)
+{
+ if (vmci_dev->mmio_base == NULL)
+ ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
+ dest, size);
+ else {
+ /*
+ * For DMA datagrams, the data_buffer will contain the header on the
+ * first page, followed by the incoming datagram(s) on the following
+ * pages. The header uses an S/G element immediately following the
+ * header on the first page to point to the data area.
+ */
+ struct vmci_data_in_out_header *buffer_header = vmci_dev->data_buffer;
+ struct vmci_sg_elem *sg_array = (struct vmci_sg_elem *)(buffer_header + 1);
+ size_t buffer_offset = dest - vmci_dev->data_buffer;
+
+ buffer_header->opcode = 1;
+ buffer_header->size = 1;
+ buffer_header->busy = 0;
+ sg_array[0].addr = vmci_dev->data_buffer_base + buffer_offset;
+ sg_array[0].size = size;
+
+ vmci_write_reg(vmci_dev, lower_32_bits(vmci_dev->data_buffer_base),
+ VMCI_DATA_IN_LOW_ADDR);
+
+ wait_event(vmci_dev->inout_wq, buffer_header->busy == 1);
+ }
+}
+
+static int vmci_write_data(struct vmci_guest_device *dev,
+ struct vmci_datagram *dg)
+{
+ int result;
+
+ if (dev->mmio_base != NULL) {
+ struct vmci_data_in_out_header *buffer_header = dev->tx_buffer;
+ u8 *dg_out_buffer = (u8 *)(buffer_header + 1);
+
+ if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ /*
+ * Initialize send buffer with outgoing datagram
+ * and set up header for inline data. Device will
+ * not access buffer asynchronously - only after
+ * the write to VMCI_DATA_OUT_LOW_ADDR.
+ */
+ memcpy(dg_out_buffer, dg, VMCI_DG_SIZE(dg));
+ buffer_header->opcode = 0;
+ buffer_header->size = VMCI_DG_SIZE(dg);
+ buffer_header->busy = 1;
+
+ vmci_write_reg(dev, lower_32_bits(dev->tx_buffer_base),
+ VMCI_DATA_OUT_LOW_ADDR);
+
+ /* Caller holds a spinlock, so cannot block. */
+ spin_until_cond(buffer_header->busy == 0);
+
+ result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR);
+ if (result == VMCI_SUCCESS)
+ result = (int)buffer_header->result;
+ } else {
+ iowrite8_rep(dev->iobase + VMCI_DATA_OUT_ADDR,
+ dg, VMCI_DG_SIZE(dg));
+ result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR);
+ }
+
+ return result;
+}
+
/*
* VM to hypervisor call mechanism. We use the standard VMware naming
* convention since shared code is calling this function as well.
@@ -114,9 +212,8 @@ int vmci_send_datagram(struct vmci_datagram *dg)
spin_lock_irqsave(&vmci_dev_spinlock, flags);
if (vmci_dev_g) {
- iowrite8_rep(vmci_dev_g->iobase + VMCI_DATA_OUT_ADDR,
- dg, VMCI_DG_SIZE(dg));
- result = ioread32(vmci_dev_g->iobase + VMCI_RESULT_LOW_ADDR);
+ vmci_write_data(vmci_dev_g, dg);
+ result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR);
} else {
result = VMCI_ERROR_UNAVAILABLE;
}
@@ -156,9 +253,9 @@ static void vmci_guest_cid_update(u32 sub_id,
/*
* Verify that the host supports the hypercalls we need. If it does not,
- * try to find fallback hypercalls and use those instead. Returns
- * true if required hypercalls (or fallback hypercalls) are
- * supported by the host, false otherwise.
+ * try to find fallback hypercalls and use those instead. Returns 0 if
+ * required hypercalls (or fallback hypercalls) are supported by the host,
+ * an error code otherwise.
*/
static int vmci_check_host_caps(struct pci_dev *pdev)
{
@@ -195,15 +292,17 @@ static int vmci_check_host_caps(struct pci_dev *pdev)
}
/*
- * Reads datagrams from the data in port and dispatches them. We
- * always start reading datagrams into only the first page of the
- * datagram buffer. If the datagrams don't fit into one page, we
- * use the maximum datagram buffer size for the remainder of the
- * invocation. This is a simple heuristic for not penalizing
- * small datagrams.
+ * Reads datagrams from the device and dispatches them. For IO port
+ * based access to the device, we always start reading datagrams into
+ * only the first page of the datagram buffer. If the datagrams don't
+ * fit into one page, we use the maximum datagram buffer size for the
+ * remainder of the invocation. This is a simple heuristic for not
+ * penalizing small datagrams. For DMA-based datagrams, we always
+ * use the maximum datagram buffer size, since there is no performance
+ * penalty for doing so.
*
* This function assumes that it has exclusive access to the data
- * in port for the duration of the call.
+ * in register(s) for the duration of the call.
*/
static void vmci_dispatch_dgs(unsigned long data)
{
@@ -211,23 +310,41 @@ static void vmci_dispatch_dgs(unsigned long data)
u8 *dg_in_buffer = vmci_dev->data_buffer;
struct vmci_datagram *dg;
size_t dg_in_buffer_size = VMCI_MAX_DG_SIZE;
- size_t current_dg_in_buffer_size = PAGE_SIZE;
+ size_t current_dg_in_buffer_size;
size_t remaining_bytes;
+ bool is_io_port = vmci_dev->mmio_base == NULL;
BUILD_BUG_ON(VMCI_MAX_DG_SIZE < PAGE_SIZE);
- ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
- vmci_dev->data_buffer, current_dg_in_buffer_size);
+ if (!is_io_port) {
+ /* For mmio, the first page is used for the header. */
+ dg_in_buffer += PAGE_SIZE;
+
+ /*
+ * For DMA-based datagram operations, there is no performance
+ * penalty for reading the maximum buffer size.
+ */
+ current_dg_in_buffer_size = VMCI_MAX_DG_SIZE;
+ } else {
+ current_dg_in_buffer_size = PAGE_SIZE;
+ }
+ vmci_read_data(vmci_dev, dg_in_buffer, current_dg_in_buffer_size);
dg = (struct vmci_datagram *)dg_in_buffer;
remaining_bytes = current_dg_in_buffer_size;
+ /*
+ * Read through the buffer until an invalid datagram header is
+ * encountered. The exit condition for datagrams read through
+ * VMCI_DATA_IN_ADDR is a bit more complicated, since a datagram
+ * can start on any page boundary in the buffer.
+ */
while (dg->dst.resource != VMCI_INVALID_ID ||
- remaining_bytes > PAGE_SIZE) {
+ (is_io_port && remaining_bytes > PAGE_SIZE)) {
unsigned dg_in_size;
/*
- * When the input buffer spans multiple pages, a datagram can
- * start on any page boundary in the buffer.
+ * If using VMCI_DATA_IN_ADDR, skip to the next page
+ * as a datagram can start on any page boundary.
*/
if (dg->dst.resource == VMCI_INVALID_ID) {
dg = (struct vmci_datagram *)roundup(
@@ -277,11 +394,10 @@ static void vmci_dispatch_dgs(unsigned long data)
current_dg_in_buffer_size =
dg_in_buffer_size;
- ioread8_rep(vmci_dev->iobase +
- VMCI_DATA_IN_ADDR,
- vmci_dev->data_buffer +
+ vmci_read_data(vmci_dev,
+ dg_in_buffer +
remaining_bytes,
- current_dg_in_buffer_size -
+ current_dg_in_buffer_size -
remaining_bytes);
}
@@ -319,10 +435,8 @@ static void vmci_dispatch_dgs(unsigned long data)
current_dg_in_buffer_size = dg_in_buffer_size;
for (;;) {
- ioread8_rep(vmci_dev->iobase +
- VMCI_DATA_IN_ADDR,
- vmci_dev->data_buffer,
- current_dg_in_buffer_size);
+ vmci_read_data(vmci_dev, dg_in_buffer,
+ current_dg_in_buffer_size);
if (bytes_to_skip <= current_dg_in_buffer_size)
break;
@@ -339,8 +453,7 @@ static void vmci_dispatch_dgs(unsigned long data)
if (remaining_bytes < VMCI_DG_HEADERSIZE) {
/* Get the next batch of datagrams. */
- ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
- vmci_dev->data_buffer,
+ vmci_read_data(vmci_dev, dg_in_buffer,
current_dg_in_buffer_size);
dg = (struct vmci_datagram *)dg_in_buffer;
remaining_bytes = current_dg_in_buffer_size;
@@ -384,7 +497,7 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev)
unsigned int icr;
/* Acknowledge interrupt and determine what needs doing. */
- icr = ioread32(dev->iobase + VMCI_ICR_ADDR);
+ icr = vmci_read_reg(dev, VMCI_ICR_ADDR);
if (icr == 0 || icr == ~0)
return IRQ_NONE;
@@ -398,6 +511,12 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev)
icr &= ~VMCI_ICR_NOTIFICATION;
}
+
+ if (icr & VMCI_ICR_DMA_DATAGRAM) {
+ wake_up_all(&dev->inout_wq);
+ icr &= ~VMCI_ICR_DMA_DATAGRAM;
+ }
+
if (icr != 0)
dev_warn(dev->dev,
"Ignoring unknown interrupt cause (%d)\n",
@@ -423,13 +542,47 @@ static irqreturn_t vmci_interrupt_bm(int irq, void *_dev)
}
/*
+ * Interrupt handler for MSI-X interrupt vector VMCI_INTR_DMA_DATAGRAM,
+ * which is for the completion of a DMA datagram send or receive operation.
+ * Will only get called if we are using MSI-X with exclusive vectors.
+ */
+static irqreturn_t vmci_interrupt_dma_datagram(int irq, void *_dev)
+{
+ struct vmci_guest_device *dev = _dev;
+
+ wake_up_all(&dev->inout_wq);
+
+ return IRQ_HANDLED;
+}
+
+static void vmci_free_dg_buffers(struct vmci_guest_device *vmci_dev)
+{
+ if (vmci_dev->mmio_base != NULL) {
+ if (vmci_dev->tx_buffer != NULL)
+ dma_free_coherent(vmci_dev->dev,
+ VMCI_DMA_DG_BUFFER_SIZE,
+ vmci_dev->tx_buffer,
+ vmci_dev->tx_buffer_base);
+ if (vmci_dev->data_buffer != NULL)
+ dma_free_coherent(vmci_dev->dev,
+ VMCI_DMA_DG_BUFFER_SIZE,
+ vmci_dev->data_buffer,
+ vmci_dev->data_buffer_base);
+ } else {
+ vfree(vmci_dev->data_buffer);
+ }
+}
+
+/*
* Most of the initialization at module load time is done here.
*/
static int vmci_guest_probe_device(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct vmci_guest_device *vmci_dev;
- void __iomem *iobase;
+ void __iomem *iobase = NULL;
+ void __iomem *mmio_base = NULL;
+ unsigned int num_irq_vectors;
unsigned int capabilities;
unsigned int caps_in_use;
unsigned long cmd;
@@ -445,16 +598,29 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
return error;
}
- error = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
- if (error) {
- dev_err(&pdev->dev, "Failed to reserve/map IO regions\n");
- return error;
- }
+ /*
+ * The VMCI device with mmio access to registers requests 256KB
+ * for BAR1. If present, driver will use new VMCI device
+ * functionality for register access and datagram send/recv.
+ */
- iobase = pcim_iomap_table(pdev)[0];
+ if (pci_resource_len(pdev, 1) == VMCI_WITH_MMIO_ACCESS_BAR_SIZE) {
+ dev_info(&pdev->dev, "MMIO register access is available\n");
+ mmio_base = pci_iomap_range(pdev, 1, VMCI_MMIO_ACCESS_OFFSET,
+ VMCI_MMIO_ACCESS_SIZE);
+ /* If the map fails, we fall back to IOIO access. */
+ if (!mmio_base)
+ dev_warn(&pdev->dev, "Failed to map MMIO register access\n");
+ }
- dev_info(&pdev->dev, "Found VMCI PCI device at %#lx, irq %u\n",
- (unsigned long)iobase, pdev->irq);
+ if (!mmio_base) {
+ error = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to reserve/map IO regions\n");
+ return error;
+ }
+ iobase = pcim_iomap_table(pdev)[0];
+ }
vmci_dev = devm_kzalloc(&pdev->dev, sizeof(*vmci_dev), GFP_KERNEL);
if (!vmci_dev) {
@@ -466,17 +632,35 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
vmci_dev->dev = &pdev->dev;
vmci_dev->exclusive_vectors = false;
vmci_dev->iobase = iobase;
+ vmci_dev->mmio_base = mmio_base;
tasklet_init(&vmci_dev->datagram_tasklet,
vmci_dispatch_dgs, (unsigned long)vmci_dev);
tasklet_init(&vmci_dev->bm_tasklet,
vmci_process_bitmap, (unsigned long)vmci_dev);
+ init_waitqueue_head(&vmci_dev->inout_wq);
- vmci_dev->data_buffer = vmalloc(VMCI_MAX_DG_SIZE);
+ if (mmio_base != NULL) {
+ vmci_dev->tx_buffer = dma_alloc_coherent(&pdev->dev, VMCI_DMA_DG_BUFFER_SIZE,
+ &vmci_dev->tx_buffer_base,
+ GFP_KERNEL);
+ if (!vmci_dev->tx_buffer) {
+ dev_err(&pdev->dev,
+ "Can't allocate memory for datagram tx buffer\n");
+ return -ENOMEM;
+ }
+
+ vmci_dev->data_buffer = dma_alloc_coherent(&pdev->dev, VMCI_DMA_DG_BUFFER_SIZE,
+ &vmci_dev->data_buffer_base,
+ GFP_KERNEL);
+ } else {
+ vmci_dev->data_buffer = vmalloc(VMCI_MAX_DG_SIZE);
+ }
if (!vmci_dev->data_buffer) {
dev_err(&pdev->dev,
"Can't allocate memory for datagram buffer\n");
- return -ENOMEM;
+ error = -ENOMEM;
+ goto err_free_data_buffers;
}
pci_set_master(pdev); /* To enable queue_pair functionality. */
@@ -490,11 +674,11 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
*
* Right now, we need datagrams. There are no fallbacks.
*/
- capabilities = ioread32(vmci_dev->iobase + VMCI_CAPS_ADDR);
+ capabilities = vmci_read_reg(vmci_dev, VMCI_CAPS_ADDR);
if (!(capabilities & VMCI_CAPS_DATAGRAM)) {
dev_err(&pdev->dev, "Device does not support datagrams\n");
error = -ENXIO;
- goto err_free_data_buffer;
+ goto err_free_data_buffers;
}
caps_in_use = VMCI_CAPS_DATAGRAM;
@@ -522,19 +706,39 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
vmci_dev->notification_bitmap = dma_alloc_coherent(
&pdev->dev, PAGE_SIZE, &vmci_dev->notification_base,
GFP_KERNEL);
- if (!vmci_dev->notification_bitmap) {
+ if (!vmci_dev->notification_bitmap)
dev_warn(&pdev->dev,
"Unable to allocate notification bitmap\n");
- } else {
- memset(vmci_dev->notification_bitmap, 0, PAGE_SIZE);
+ else
caps_in_use |= VMCI_CAPS_NOTIFICATIONS;
+ }
+
+ if (mmio_base != NULL) {
+ if (capabilities & VMCI_CAPS_DMA_DATAGRAM) {
+ caps_in_use |= VMCI_CAPS_DMA_DATAGRAM;
+ } else {
+ dev_err(&pdev->dev,
+ "Missing capability: VMCI_CAPS_DMA_DATAGRAM\n");
+ error = -ENXIO;
+ goto err_free_notification_bitmap;
}
}
dev_info(&pdev->dev, "Using capabilities 0x%x\n", caps_in_use);
/* Let the host know which capabilities we intend to use. */
- iowrite32(caps_in_use, vmci_dev->iobase + VMCI_CAPS_ADDR);
+ vmci_write_reg(vmci_dev, caps_in_use, VMCI_CAPS_ADDR);
+
+ if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM) {
+ /* Let the device know the size for pages passed down. */
+ vmci_write_reg(vmci_dev, PAGE_SHIFT, VMCI_GUEST_PAGE_SHIFT);
+
+ /* Configure the high order parts of the data in/out buffers. */
+ vmci_write_reg(vmci_dev, upper_32_bits(vmci_dev->data_buffer_base),
+ VMCI_DATA_IN_HIGH_ADDR);
+ vmci_write_reg(vmci_dev, upper_32_bits(vmci_dev->tx_buffer_base),
+ VMCI_DATA_OUT_HIGH_ADDR);
+ }
/* Set up global device so that we can start sending datagrams */
spin_lock_irq(&vmci_dev_spinlock);
@@ -561,7 +765,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
/* Check host capabilities. */
error = vmci_check_host_caps(pdev);
if (error)
- goto err_remove_bitmap;
+ goto err_remove_vmci_dev_g;
/* Enable device. */
@@ -581,13 +785,17 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* Enable interrupts. Try MSI-X first, then MSI, and then fallback on
* legacy interrupts.
*/
- error = pci_alloc_irq_vectors(pdev, VMCI_MAX_INTRS, VMCI_MAX_INTRS,
- PCI_IRQ_MSIX);
+ if (vmci_dev->mmio_base != NULL)
+ num_irq_vectors = VMCI_MAX_INTRS;
+ else
+ num_irq_vectors = VMCI_MAX_INTRS_NOTIFICATION;
+ error = pci_alloc_irq_vectors(pdev, num_irq_vectors, num_irq_vectors,
+ PCI_IRQ_MSIX);
if (error < 0) {
error = pci_alloc_irq_vectors(pdev, 1, 1,
PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY);
if (error < 0)
- goto err_remove_bitmap;
+ goto err_unsubscribe_event;
} else {
vmci_dev->exclusive_vectors = true;
}
@@ -620,6 +828,17 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
pci_irq_vector(pdev, 1), error);
goto err_free_irq;
}
+ if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM) {
+ error = request_irq(pci_irq_vector(pdev, 2),
+ vmci_interrupt_dma_datagram,
+ 0, KBUILD_MODNAME, vmci_dev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Failed to allocate irq %u: %d\n",
+ pci_irq_vector(pdev, 2), error);
+ goto err_free_bm_irq;
+ }
+ }
}
dev_dbg(&pdev->dev, "Registered device\n");
@@ -630,17 +849,22 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
cmd = VMCI_IMR_DATAGRAM;
if (caps_in_use & VMCI_CAPS_NOTIFICATIONS)
cmd |= VMCI_IMR_NOTIFICATION;
- iowrite32(cmd, vmci_dev->iobase + VMCI_IMR_ADDR);
+ if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM)
+ cmd |= VMCI_IMR_DMA_DATAGRAM;
+ vmci_write_reg(vmci_dev, cmd, VMCI_IMR_ADDR);
/* Enable interrupts. */
- iowrite32(VMCI_CONTROL_INT_ENABLE,
- vmci_dev->iobase + VMCI_CONTROL_ADDR);
+ vmci_write_reg(vmci_dev, VMCI_CONTROL_INT_ENABLE, VMCI_CONTROL_ADDR);
pci_set_drvdata(pdev, vmci_dev);
vmci_call_vsock_callback(false);
return 0;
+err_free_bm_irq:
+ if (vmci_dev->exclusive_vectors)
+ free_irq(pci_irq_vector(pdev, 1), vmci_dev);
+
err_free_irq:
free_irq(pci_irq_vector(pdev, 0), vmci_dev);
tasklet_kill(&vmci_dev->datagram_tasklet);
@@ -649,29 +873,29 @@ err_free_irq:
err_disable_msi:
pci_free_irq_vectors(pdev);
+err_unsubscribe_event:
vmci_err = vmci_event_unsubscribe(ctx_update_sub_id);
if (vmci_err < VMCI_SUCCESS)
dev_warn(&pdev->dev,
"Failed to unsubscribe from event (type=%d) with subscriber (ID=0x%x): %d\n",
VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id, vmci_err);
-err_remove_bitmap:
- if (vmci_dev->notification_bitmap) {
- iowrite32(VMCI_CONTROL_RESET,
- vmci_dev->iobase + VMCI_CONTROL_ADDR);
- dma_free_coherent(&pdev->dev, PAGE_SIZE,
- vmci_dev->notification_bitmap,
- vmci_dev->notification_base);
- }
-
err_remove_vmci_dev_g:
spin_lock_irq(&vmci_dev_spinlock);
vmci_pdev = NULL;
vmci_dev_g = NULL;
spin_unlock_irq(&vmci_dev_spinlock);
-err_free_data_buffer:
- vfree(vmci_dev->data_buffer);
+err_free_notification_bitmap:
+ if (vmci_dev->notification_bitmap) {
+ vmci_write_reg(vmci_dev, VMCI_CONTROL_RESET, VMCI_CONTROL_ADDR);
+ dma_free_coherent(&pdev->dev, PAGE_SIZE,
+ vmci_dev->notification_bitmap,
+ vmci_dev->notification_base);
+ }
+
+err_free_data_buffers:
+ vmci_free_dg_buffers(vmci_dev);
/* The rest are managed resources and will be freed by PCI core */
return error;
@@ -700,15 +924,18 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
spin_unlock_irq(&vmci_dev_spinlock);
dev_dbg(&pdev->dev, "Resetting vmci device\n");
- iowrite32(VMCI_CONTROL_RESET, vmci_dev->iobase + VMCI_CONTROL_ADDR);
+ vmci_write_reg(vmci_dev, VMCI_CONTROL_RESET, VMCI_CONTROL_ADDR);
/*
* Free IRQ and then disable MSI/MSI-X as appropriate. For
* MSI-X, we might have multiple vectors, each with their own
* IRQ, which we must free too.
*/
- if (vmci_dev->exclusive_vectors)
+ if (vmci_dev->exclusive_vectors) {
free_irq(pci_irq_vector(pdev, 1), vmci_dev);
+ if (vmci_dev->mmio_base != NULL)
+ free_irq(pci_irq_vector(pdev, 2), vmci_dev);
+ }
free_irq(pci_irq_vector(pdev, 0), vmci_dev);
pci_free_irq_vectors(pdev);
@@ -726,7 +953,10 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
vmci_dev->notification_base);
}
- vfree(vmci_dev->data_buffer);
+ vmci_free_dg_buffers(vmci_dev);
+
+ if (vmci_dev->mmio_base != NULL)
+ pci_iounmap(pdev, vmci_dev->mmio_base);
/* The rest are managed resources and will be freed by PCI core */
}
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 219029224727..e1580f78c6b2 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1504,12 +1504,12 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
realtek_init_host(host);
- if (pcr->rtd3_en) {
- pm_runtime_set_autosuspend_delay(&pdev->dev, 5000);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
- }
-
+ pm_runtime_no_callbacks(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 200);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_use_autosuspend(&pdev->dev);
mmc_add_host(mmc);
@@ -1530,11 +1530,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
pcr->slots[RTSX_SD_CARD].card_event = NULL;
mmc = host->mmc;
- if (pcr->rtd3_en) {
- pm_runtime_dont_use_autosuspend(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- }
-
cancel_work_sync(&host->work);
mutex_lock(&host->host_mutex);
@@ -1557,6 +1552,9 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
flush_work(&host->work);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
mmc_free_host(mmc);
dev_dbg(&(pdev->dev),
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index c5c3e9387647..7731796024e0 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -725,8 +725,7 @@ int del_mtd_device(struct mtd_info *mtd)
debugfs_remove_recursive(mtd->dbg.dfs_dir);
/* Try to remove the NVMEM provider */
- if (mtd->nvmem)
- nvmem_unregister(mtd->nvmem);
+ nvmem_unregister(mtd->nvmem);
device_unregister(&mtd->dev);
@@ -905,8 +904,7 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd)
return 0;
err:
- if (mtd->otp_user_nvmem)
- nvmem_unregister(mtd->otp_user_nvmem);
+ nvmem_unregister(mtd->otp_user_nvmem);
return err;
}
@@ -1010,11 +1008,8 @@ int mtd_device_unregister(struct mtd_info *master)
memset(&master->reboot_notifier, 0, sizeof(master->reboot_notifier));
}
- if (master->otp_user_nvmem)
- nvmem_unregister(master->otp_user_nvmem);
-
- if (master->otp_factory_nvmem)
- nvmem_unregister(master->otp_factory_nvmem);
+ nvmem_unregister(master->otp_user_nvmem);
+ nvmem_unregister(master->otp_factory_nvmem);
err = del_mtd_partitions(master);
if (err)
diff --git a/drivers/mux/core.c b/drivers/mux/core.c
index 22f4709768d1..49bedbe6316c 100644
--- a/drivers/mux/core.c
+++ b/drivers/mux/core.c
@@ -29,6 +29,20 @@
*/
#define MUX_CACHE_UNKNOWN MUX_IDLE_AS_IS
+/**
+ * struct mux_state - Represents a mux controller state specific to a given
+ * consumer.
+ * @mux: Pointer to a mux controller.
+ * @state: State of the mux to be selected.
+ *
+ * This structure is specific to the consumer that acquires it and has
+ * information specific to that consumer.
+ */
+struct mux_state {
+ struct mux_control *mux;
+ unsigned int state;
+};
+
static struct class mux_class = {
.name = "mux",
.owner = THIS_MODULE,
@@ -341,7 +355,8 @@ static void mux_control_delay(struct mux_control *mux, unsigned int delay_us)
* On successfully selecting the mux-control state, it will be locked until
* there is a call to mux_control_deselect(). If the mux-control is already
* selected when mux_control_select() is called, the caller will be blocked
- * until mux_control_deselect() is called (by someone else).
+ * until mux_control_deselect() or mux_state_deselect() is called (by someone
+ * else).
*
* Therefore, make sure to call mux_control_deselect() when the operation is
* complete and the mux-control is free for others to use, but do not call
@@ -371,13 +386,37 @@ int mux_control_select_delay(struct mux_control *mux, unsigned int state,
EXPORT_SYMBOL_GPL(mux_control_select_delay);
/**
+ * mux_state_select_delay() - Select the given multiplexer state.
+ * @mstate: The mux-state to select.
+ * @delay_us: The time to delay (in microseconds) if the mux state is changed.
+ *
+ * On successfully selecting the mux-state, its mux-control will be locked
+ * until there is a call to mux_state_deselect(). If the mux-control is already
+ * selected when mux_state_select() is called, the caller will be blocked
+ * until mux_state_deselect() or mux_control_deselect() is called (by someone
+ * else).
+ *
+ * Therefore, make sure to call mux_state_deselect() when the operation is
+ * complete and the mux-control is free for others to use, but do not call
+ * mux_state_deselect() if mux_state_select() fails.
+ *
+ * Return: 0 when the mux-state has been selected or a negative
+ * errno on error.
+ */
+int mux_state_select_delay(struct mux_state *mstate, unsigned int delay_us)
+{
+ return mux_control_select_delay(mstate->mux, mstate->state, delay_us);
+}
+EXPORT_SYMBOL_GPL(mux_state_select_delay);
+
+/**
* mux_control_try_select_delay() - Try to select the given multiplexer state.
* @mux: The mux-control to request a change of state from.
* @state: The new requested state.
* @delay_us: The time to delay (in microseconds) if the mux state is changed.
*
* On successfully selecting the mux-control state, it will be locked until
- * mux_control_deselect() called.
+ * mux_control_deselect() is called.
*
* Therefore, make sure to call mux_control_deselect() when the operation is
* complete and the mux-control is free for others to use, but do not call
@@ -406,6 +445,27 @@ int mux_control_try_select_delay(struct mux_control *mux, unsigned int state,
EXPORT_SYMBOL_GPL(mux_control_try_select_delay);
/**
+ * mux_state_try_select_delay() - Try to select the given multiplexer state.
+ * @mstate: The mux-state to select.
+ * @delay_us: The time to delay (in microseconds) if the mux state is changed.
+ *
+ * On successfully selecting the mux-state, its mux-control will be locked
+ * until mux_state_deselect() is called.
+ *
+ * Therefore, make sure to call mux_state_deselect() when the operation is
+ * complete and the mux-control is free for others to use, but do not call
+ * mux_state_deselect() if mux_state_try_select() fails.
+ *
+ * Return: 0 when the mux-state has been selected or a negative errno on
+ * error. Specifically -EBUSY if the mux-control is contended.
+ */
+int mux_state_try_select_delay(struct mux_state *mstate, unsigned int delay_us)
+{
+ return mux_control_try_select_delay(mstate->mux, mstate->state, delay_us);
+}
+EXPORT_SYMBOL_GPL(mux_state_try_select_delay);
+
+/**
* mux_control_deselect() - Deselect the previously selected multiplexer state.
* @mux: The mux-control to deselect.
*
@@ -431,6 +491,24 @@ int mux_control_deselect(struct mux_control *mux)
}
EXPORT_SYMBOL_GPL(mux_control_deselect);
+/**
+ * mux_state_deselect() - Deselect the previously selected multiplexer state.
+ * @mstate: The mux-state to deselect.
+ *
+ * It is required that a single call is made to mux_state_deselect() for
+ * each and every successful call made to either of mux_state_select() or
+ * mux_state_try_select().
+ *
+ * Return: 0 on success and a negative errno on error. An error can only
+ * occur if the mux has an idle state. Note that even if an error occurs, the
+ * mux-control is unlocked and is thus free for the next access.
+ */
+int mux_state_deselect(struct mux_state *mstate)
+{
+ return mux_control_deselect(mstate->mux);
+}
+EXPORT_SYMBOL_GPL(mux_state_deselect);
+
/* Note this function returns a reference to the mux_chip dev. */
static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
{
@@ -441,14 +519,17 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
return dev ? to_mux_chip(dev) : NULL;
}
-/**
- * mux_control_get() - Get the mux-control for a device.
+/*
+ * mux_get() - Get the mux-control for a device.
* @dev: The device that needs a mux-control.
* @mux_name: The name identifying the mux-control.
+ * @state: Pointer to where the requested state is returned, or NULL when
+ * the required multiplexer states are handled by other means.
*
* Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
*/
-struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
+static struct mux_control *mux_get(struct device *dev, const char *mux_name,
+ unsigned int *state)
{
struct device_node *np = dev->of_node;
struct of_phandle_args args;
@@ -458,8 +539,12 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
int ret;
if (mux_name) {
- index = of_property_match_string(np, "mux-control-names",
- mux_name);
+ if (state)
+ index = of_property_match_string(np, "mux-state-names",
+ mux_name);
+ else
+ index = of_property_match_string(np, "mux-control-names",
+ mux_name);
if (index < 0) {
dev_err(dev, "mux controller '%s' not found\n",
mux_name);
@@ -467,12 +552,17 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
}
}
- ret = of_parse_phandle_with_args(np,
- "mux-controls", "#mux-control-cells",
- index, &args);
+ if (state)
+ ret = of_parse_phandle_with_args(np,
+ "mux-states", "#mux-state-cells",
+ index, &args);
+ else
+ ret = of_parse_phandle_with_args(np,
+ "mux-controls", "#mux-control-cells",
+ index, &args);
if (ret) {
- dev_err(dev, "%pOF: failed to get mux-control %s(%i)\n",
- np, mux_name ?: "", index);
+ dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
+ np, state ? "state" : "control", mux_name ?: "", index);
return ERR_PTR(ret);
}
@@ -481,17 +571,35 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
if (!mux_chip)
return ERR_PTR(-EPROBE_DEFER);
- if (args.args_count > 1 ||
- (!args.args_count && (mux_chip->controllers > 1))) {
- dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n",
- np, args.np);
- put_device(&mux_chip->dev);
- return ERR_PTR(-EINVAL);
- }
-
controller = 0;
- if (args.args_count)
- controller = args.args[0];
+ if (state) {
+ if (args.args_count > 2 || args.args_count == 0 ||
+ (args.args_count < 2 && mux_chip->controllers > 1)) {
+ dev_err(dev, "%pOF: wrong #mux-state-cells for %pOF\n",
+ np, args.np);
+ put_device(&mux_chip->dev);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (args.args_count == 2) {
+ controller = args.args[0];
+ *state = args.args[1];
+ } else {
+ *state = args.args[0];
+ }
+
+ } else {
+ if (args.args_count > 1 ||
+ (!args.args_count && mux_chip->controllers > 1)) {
+ dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n",
+ np, args.np);
+ put_device(&mux_chip->dev);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (args.args_count)
+ controller = args.args[0];
+ }
if (controller >= mux_chip->controllers) {
dev_err(dev, "%pOF: bad mux controller %u specified in %pOF\n",
@@ -502,6 +610,18 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
return &mux_chip->mux[controller];
}
+
+/**
+ * mux_control_get() - Get the mux-control for a device.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
+{
+ return mux_get(dev, mux_name, NULL);
+}
EXPORT_SYMBOL_GPL(mux_control_get);
/**
@@ -554,6 +674,81 @@ struct mux_control *devm_mux_control_get(struct device *dev,
EXPORT_SYMBOL_GPL(devm_mux_control_get);
/*
+ * mux_state_get() - Get the mux-state for a device.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno.
+ */
+static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
+{
+ struct mux_state *mstate;
+
+ mstate = kzalloc(sizeof(*mstate), GFP_KERNEL);
+ if (!mstate)
+ return ERR_PTR(-ENOMEM);
+
+ mstate->mux = mux_get(dev, mux_name, &mstate->state);
+ if (IS_ERR(mstate->mux)) {
+ int err = PTR_ERR(mstate->mux);
+
+ kfree(mstate);
+ return ERR_PTR(err);
+ }
+
+ return mstate;
+}
+
+/*
+ * mux_state_put() - Put away the mux-state for good.
+ * @mstate: The mux-state to put away.
+ *
+ * mux_state_put() reverses the effects of mux_state_get().
+ */
+static void mux_state_put(struct mux_state *mstate)
+{
+ mux_control_put(mstate->mux);
+ kfree(mstate);
+}
+
+static void devm_mux_state_release(struct device *dev, void *res)
+{
+ struct mux_state *mstate = *(struct mux_state **)res;
+
+ mux_state_put(mstate);
+}
+
+/**
+ * devm_mux_state_get() - Get the mux-state for a device, with resource
+ * management.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
+ */
+struct mux_state *devm_mux_state_get(struct device *dev,
+ const char *mux_name)
+{
+ struct mux_state **ptr, *mstate;
+
+ ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ mstate = mux_state_get(dev, mux_name);
+ if (IS_ERR(mstate)) {
+ devres_free(ptr);
+ return mstate;
+ }
+
+ *ptr = mstate;
+ devres_add(dev, ptr);
+
+ return mstate;
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get);
+
+/*
* Using subsys_initcall instead of module_init here to try to ensure - for
* the non-modular case - that the subsystem is initialized when mux consumers
* and mux controllers start to use it.
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index da414617a54d..555aa77a574d 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -300,4 +300,28 @@ config NVMEM_BRCM_NVRAM
This driver provides support for Broadcom's NVRAM that can be accessed
using I/O mapping.
+config NVMEM_LAYERSCAPE_SFP
+ tristate "Layerscape SFP (Security Fuse Processor) support"
+ depends on ARCH_LAYERSCAPE || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This driver provides support to read the eFuses on Freescale
+ Layerscape SoC's. For example, the vendor provides a per part
+ unique ID there.
+
+ This driver can also be built as a module. If so, the module
+ will be called layerscape-sfp.
+
+config NVMEM_SUNPLUS_OCOTP
+ tristate "Sunplus SoC OTP support"
+ depends on SOC_SP7021 || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This is a driver for the On-chip OTP controller (OCOTP) available
+ on Sunplus SoCs. It provides access to 128 bytes of one-time
+ programmable eFuse.
+
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-sunplus-ocotp.
+
endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index dcbbde35b6a8..891958e29d25 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -61,3 +61,7 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
nvmem-rmem-y := rmem.o
obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
nvmem_brcm_nvram-y := brcm_nvram.o
+obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
+nvmem-layerscape-sfp-y := layerscape-sfp.o
+obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o
+nvmem_sunplus_ocotp-y := sunplus-ocotp.o
diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
index bd2ecaaf4585..439f00b9eef6 100644
--- a/drivers/nvmem/brcm_nvram.c
+++ b/drivers/nvmem/brcm_nvram.c
@@ -6,12 +6,26 @@
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define NVRAM_MAGIC "FLSH"
struct brcm_nvram {
struct device *dev;
void __iomem *base;
+ struct nvmem_cell_info *cells;
+ int ncells;
+};
+
+struct brcm_nvram_header {
+ char magic[4];
+ __le32 len;
+ __le32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
+ __le32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
+ __le32 config_ncdl; /* ncdl values for memc */
};
static int brcm_nvram_read(void *context, unsigned int offset, void *val,
@@ -26,6 +40,75 @@ static int brcm_nvram_read(void *context, unsigned int offset, void *val,
return 0;
}
+static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
+ size_t len)
+{
+ struct device *dev = priv->dev;
+ char *var, *value, *eq;
+ int idx;
+
+ priv->ncells = 0;
+ for (var = data + sizeof(struct brcm_nvram_header);
+ var < (char *)data + len && *var;
+ var += strlen(var) + 1) {
+ priv->ncells++;
+ }
+
+ priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
+ if (!priv->cells)
+ return -ENOMEM;
+
+ for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
+ var < (char *)data + len && *var;
+ var = value + strlen(value) + 1, idx++) {
+ eq = strchr(var, '=');
+ if (!eq)
+ break;
+ *eq = '\0';
+ value = eq + 1;
+
+ priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
+ if (!priv->cells[idx].name)
+ return -ENOMEM;
+ priv->cells[idx].offset = value - (char *)data;
+ priv->cells[idx].bytes = strlen(value);
+ }
+
+ return 0;
+}
+
+static int brcm_nvram_parse(struct brcm_nvram *priv)
+{
+ struct device *dev = priv->dev;
+ struct brcm_nvram_header header;
+ uint8_t *data;
+ size_t len;
+ int err;
+
+ memcpy_fromio(&header, priv->base, sizeof(header));
+
+ if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
+ dev_err(dev, "Invalid NVRAM magic\n");
+ return -EINVAL;
+ }
+
+ len = le32_to_cpu(header.len);
+
+ data = kcalloc(1, len, GFP_KERNEL);
+ memcpy_fromio(data, priv->base, len);
+ data[len - 1] = '\0';
+
+ err = brcm_nvram_add_cells(priv, data, len);
+ if (err) {
+ dev_err(dev, "Failed to add cells: %d\n", err);
+ return err;
+ }
+
+ kfree(data);
+
+ return 0;
+}
+
static int brcm_nvram_probe(struct platform_device *pdev)
{
struct nvmem_config config = {
@@ -35,6 +118,7 @@ static int brcm_nvram_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct resource *res;
struct brcm_nvram *priv;
+ int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -46,7 +130,13 @@ static int brcm_nvram_probe(struct platform_device *pdev)
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
+ err = brcm_nvram_parse(priv);
+ if (err)
+ return err;
+
config.dev = dev;
+ config.cells = priv->cells;
+ config.ncells = priv->ncells;
config.priv = priv;
config.size = resource_size(res);
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 9fd1602b539d..f58d9bc7aa08 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -903,13 +903,14 @@ static void nvmem_device_release(struct kref *kref)
*/
void nvmem_unregister(struct nvmem_device *nvmem)
{
- kref_put(&nvmem->refcnt, nvmem_device_release);
+ if (nvmem)
+ kref_put(&nvmem->refcnt, nvmem_device_release);
}
EXPORT_SYMBOL_GPL(nvmem_unregister);
-static void devm_nvmem_release(struct device *dev, void *res)
+static void devm_nvmem_unregister(void *nvmem)
{
- nvmem_unregister(*(struct nvmem_device **)res);
+ nvmem_unregister(nvmem);
}
/**
@@ -926,47 +927,21 @@ static void devm_nvmem_release(struct device *dev, void *res)
struct nvmem_device *devm_nvmem_register(struct device *dev,
const struct nvmem_config *config)
{
- struct nvmem_device **ptr, *nvmem;
-
- ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
+ struct nvmem_device *nvmem;
+ int ret;
nvmem = nvmem_register(config);
+ if (IS_ERR(nvmem))
+ return nvmem;
- if (!IS_ERR(nvmem)) {
- *ptr = nvmem;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
+ ret = devm_add_action_or_reset(dev, devm_nvmem_unregister, nvmem);
+ if (ret)
+ return ERR_PTR(ret);
return nvmem;
}
EXPORT_SYMBOL_GPL(devm_nvmem_register);
-static int devm_nvmem_match(struct device *dev, void *res, void *data)
-{
- struct nvmem_device **r = res;
-
- return *r == data;
-}
-
-/**
- * devm_nvmem_unregister() - Unregister previously registered managed nvmem
- * device.
- *
- * @dev: Device that uses the nvmem device.
- * @nvmem: Pointer to previously registered nvmem device.
- *
- * Return: Will be negative on error or zero on success.
- */
-int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
-{
- return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
-}
-EXPORT_SYMBOL(devm_nvmem_unregister);
-
static struct nvmem_device *__nvmem_device_get(void *data,
int (*match)(struct device *dev, const void *data))
{
diff --git a/drivers/nvmem/layerscape-sfp.c b/drivers/nvmem/layerscape-sfp.c
new file mode 100644
index 000000000000..e591c1511e33
--- /dev/null
+++ b/drivers/nvmem/layerscape-sfp.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Layerscape SFP driver
+ *
+ * Copyright (c) 2022 Michael Walle <michael@walle.cc>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define LAYERSCAPE_SFP_OTP_OFFSET 0x0200
+
+struct layerscape_sfp_priv {
+ void __iomem *base;
+};
+
+struct layerscape_sfp_data {
+ int size;
+};
+
+static int layerscape_sfp_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct layerscape_sfp_priv *priv = context;
+
+ memcpy_fromio(val, priv->base + LAYERSCAPE_SFP_OTP_OFFSET + offset,
+ bytes);
+
+ return 0;
+}
+
+static struct nvmem_config layerscape_sfp_nvmem_config = {
+ .name = "fsl-sfp",
+ .reg_read = layerscape_sfp_read,
+};
+
+static int layerscape_sfp_probe(struct platform_device *pdev)
+{
+ const struct layerscape_sfp_data *data;
+ struct layerscape_sfp_priv *priv;
+ struct nvmem_device *nvmem;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ data = device_get_match_data(&pdev->dev);
+
+ layerscape_sfp_nvmem_config.size = data->size;
+ layerscape_sfp_nvmem_config.dev = &pdev->dev;
+ layerscape_sfp_nvmem_config.priv = priv;
+
+ nvmem = devm_nvmem_register(&pdev->dev, &layerscape_sfp_nvmem_config);
+
+ return PTR_ERR_OR_ZERO(nvmem);
+}
+
+static const struct layerscape_sfp_data ls1028a_data = {
+ .size = 0x88,
+};
+
+static const struct of_device_id layerscape_sfp_dt_ids[] = {
+ { .compatible = "fsl,ls1028a-sfp", .data = &ls1028a_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, layerscape_sfp_dt_ids);
+
+static struct platform_driver layerscape_sfp_driver = {
+ .probe = layerscape_sfp_probe,
+ .driver = {
+ .name = "layerscape_sfp",
+ .of_match_table = layerscape_sfp_dt_ids,
+ },
+};
+module_platform_driver(layerscape_sfp_driver);
+
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_DESCRIPTION("Layerscape Security Fuse Processor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c
index 07c9f38c1c60..13eb14316f46 100644
--- a/drivers/nvmem/meson-mx-efuse.c
+++ b/drivers/nvmem/meson-mx-efuse.c
@@ -209,8 +209,7 @@ static int meson_mx_efuse_probe(struct platform_device *pdev)
if (IS_ERR(efuse->base))
return PTR_ERR(efuse->base);
- efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name,
- GFP_KERNEL);
+ efuse->config.name = drvdata->name;
efuse->config.owner = THIS_MODULE;
efuse->config.dev = &pdev->dev;
efuse->config.priv = efuse;
diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
index c500d6235bf6..162132c7dab9 100644
--- a/drivers/nvmem/qfprom.c
+++ b/drivers/nvmem/qfprom.c
@@ -22,7 +22,7 @@
/* Amount of time required to hold charge to blow fuse in micro-seconds */
#define QFPROM_FUSE_BLOW_POLL_US 100
-#define QFPROM_FUSE_BLOW_TIMEOUT_US 1000
+#define QFPROM_FUSE_BLOW_TIMEOUT_US 10000
#define QFPROM_BLOW_STATUS_OFFSET 0x048
#define QFPROM_BLOW_STATUS_BUSY 0x1
@@ -244,7 +244,7 @@ err_clk_prepared:
}
/**
- * qfprom_efuse_reg_write() - Write to fuses.
+ * qfprom_reg_write() - Write to fuses.
* @context: Our driver data.
* @reg: The offset to write at.
* @_val: Pointer to data to write.
diff --git a/drivers/nvmem/sunplus-ocotp.c b/drivers/nvmem/sunplus-ocotp.c
new file mode 100644
index 000000000000..2dc59c22eb55
--- /dev/null
+++ b/drivers/nvmem/sunplus-ocotp.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * The OCOTP driver for Sunplus SP7021
+ *
+ * Copyright (C) 2019 Sunplus Technology Inc., All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+/*
+ * OTP memory
+ * Each bank contains 4 words (32 bits).
+ * Bank 0 starts at offset 0 from the base.
+ */
+
+#define OTP_WORDS_PER_BANK 4
+#define OTP_WORD_SIZE sizeof(u32)
+#define OTP_BIT_ADDR_OF_BANK (8 * OTP_WORD_SIZE * OTP_WORDS_PER_BANK)
+#define QAC628_OTP_NUM_BANKS 8
+#define QAC628_OTP_SIZE (QAC628_OTP_NUM_BANKS * OTP_WORDS_PER_BANK * OTP_WORD_SIZE)
+#define OTP_READ_TIMEOUT_US 200000
+
+/* HB_GPIO */
+#define ADDRESS_8_DATA 0x20
+
+/* OTP_RX */
+#define OTP_CONTROL_2 0x48
+#define OTP_RD_PERIOD GENMASK(15, 8)
+#define OTP_RD_PERIOD_MASK ~GENMASK(15, 8)
+#define CPU_CLOCK FIELD_PREP(OTP_RD_PERIOD, 30)
+#define SEL_BAK_KEY2 BIT(5)
+#define SEL_BAK_KEY2_MASK ~BIT(5)
+#define SW_TRIM_EN BIT(4)
+#define SW_TRIM_EN_MASK ~BIT(4)
+#define SEL_BAK_KEY BIT(3)
+#define SEL_BAK_KEY_MASK ~BIT(3)
+#define OTP_READ BIT(2)
+#define OTP_LOAD_SECURE_DATA BIT(1)
+#define OTP_LOAD_SECURE_DATA_MASK ~BIT(1)
+#define OTP_DO_CRC BIT(0)
+#define OTP_DO_CRC_MASK ~BIT(0)
+#define OTP_STATUS 0x4c
+#define OTP_READ_DONE BIT(4)
+#define OTP_READ_DONE_MASK ~BIT(4)
+#define OTP_LOAD_SECURE_DONE_MASK ~BIT(2)
+#define OTP_READ_ADDRESS 0x50
+
+enum base_type {
+ HB_GPIO,
+ OTPRX,
+ BASEMAX,
+};
+
+struct sp_ocotp_priv {
+ struct device *dev;
+ void __iomem *base[BASEMAX];
+ struct clk *clk;
+};
+
+struct sp_ocotp_data {
+ int size;
+};
+
+const struct sp_ocotp_data sp_otp_v0 = {
+ .size = QAC628_OTP_SIZE,
+};
+
+static int sp_otp_read_real(struct sp_ocotp_priv *otp, int addr, char *value)
+{
+ unsigned int addr_data;
+ unsigned int byte_shift;
+ unsigned int status;
+ int ret;
+
+ addr_data = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
+ addr_data = addr_data / OTP_WORD_SIZE;
+
+ byte_shift = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
+ byte_shift = byte_shift % OTP_WORD_SIZE;
+
+ addr = addr / (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
+ addr = addr * OTP_BIT_ADDR_OF_BANK;
+
+ writel(readl(otp->base[OTPRX] + OTP_STATUS) & OTP_READ_DONE_MASK &
+ OTP_LOAD_SECURE_DONE_MASK, otp->base[OTPRX] + OTP_STATUS);
+ writel(addr, otp->base[OTPRX] + OTP_READ_ADDRESS);
+ writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) | OTP_READ,
+ otp->base[OTPRX] + OTP_CONTROL_2);
+ writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) & SEL_BAK_KEY2_MASK & SW_TRIM_EN_MASK
+ & SEL_BAK_KEY_MASK & OTP_LOAD_SECURE_DATA_MASK & OTP_DO_CRC_MASK,
+ otp->base[OTPRX] + OTP_CONTROL_2);
+ writel((readl(otp->base[OTPRX] + OTP_CONTROL_2) & OTP_RD_PERIOD_MASK) | CPU_CLOCK,
+ otp->base[OTPRX] + OTP_CONTROL_2);
+
+ ret = readl_poll_timeout(otp->base[OTPRX] + OTP_STATUS, status,
+ status & OTP_READ_DONE, 10, OTP_READ_TIMEOUT_US);
+
+ if (ret < 0)
+ return ret;
+
+ *value = (readl(otp->base[HB_GPIO] + ADDRESS_8_DATA + addr_data * OTP_WORD_SIZE)
+ >> (8 * byte_shift)) & 0xff;
+
+ return ret;
+}
+
+static int sp_ocotp_read(void *priv, unsigned int offset, void *value, size_t bytes)
+{
+ struct sp_ocotp_priv *otp = priv;
+ unsigned int addr;
+ char *buf = value;
+ char val[4];
+ int ret;
+
+ ret = clk_enable(otp->clk);
+ if (ret)
+ return ret;
+
+ *buf = 0;
+ for (addr = offset; addr < (offset + bytes); addr++) {
+ ret = sp_otp_read_real(otp, addr, val);
+ if (ret < 0) {
+ dev_err(otp->dev, "OTP read fail:%d at %d", ret, addr);
+ goto disable_clk;
+ }
+
+ *buf++ = *val;
+ }
+
+disable_clk:
+ clk_disable(otp->clk);
+
+ return ret;
+}
+
+static struct nvmem_config sp_ocotp_nvmem_config = {
+ .name = "sp-ocotp",
+ .read_only = true,
+ .word_size = 1,
+ .size = QAC628_OTP_SIZE,
+ .stride = 1,
+ .reg_read = sp_ocotp_read,
+ .owner = THIS_MODULE,
+};
+
+static int sp_ocotp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nvmem_device *nvmem;
+ struct sp_ocotp_priv *otp;
+ struct resource *res;
+ int ret;
+
+ otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+ if (!otp)
+ return -ENOMEM;
+
+ otp->dev = dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
+ otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
+ if (IS_ERR(otp->base[HB_GPIO]))
+ return PTR_ERR(otp->base[HB_GPIO]);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
+ otp->base[OTPRX] = devm_ioremap_resource(dev, res);
+ if (IS_ERR(otp->base[OTPRX]))
+ return PTR_ERR(otp->base[OTPRX]);
+
+ otp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(otp->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(otp->clk),
+ "devm_clk_get fail\n");
+
+ ret = clk_prepare(otp->clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare clk: %d\n", ret);
+ return ret;
+ }
+
+ sp_ocotp_nvmem_config.priv = otp;
+ sp_ocotp_nvmem_config.dev = dev;
+
+ nvmem = devm_nvmem_register(dev, &sp_ocotp_nvmem_config);
+ if (IS_ERR(nvmem))
+ return dev_err_probe(&pdev->dev, PTR_ERR(nvmem),
+ "register nvmem device fail\n");
+
+ platform_set_drvdata(pdev, nvmem);
+
+ dev_dbg(dev, "banks:%d x wpb:%d x wsize:%d = %d",
+ (int)QAC628_OTP_NUM_BANKS, (int)OTP_WORDS_PER_BANK,
+ (int)OTP_WORD_SIZE, (int)QAC628_OTP_SIZE);
+
+ dev_info(dev, "by Sunplus (C) 2020");
+
+ return 0;
+}
+
+static const struct of_device_id sp_ocotp_dt_ids[] = {
+ { .compatible = "sunplus,sp7021-ocotp", .data = &sp_otp_v0 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sp_ocotp_dt_ids);
+
+static struct platform_driver sp_otp_driver = {
+ .probe = sp_ocotp_probe,
+ .driver = {
+ .name = "sunplus,sp7021-ocotp",
+ .of_match_table = sp_ocotp_dt_ids,
+ }
+};
+module_platform_driver(sp_otp_driver);
+
+MODULE_AUTHOR("Vincent Shih <vincent.sunplus@gmail.com>");
+MODULE_DESCRIPTION("Sunplus On-Chip OTP driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
index 275b9155e473..5750e1f4bcdb 100644
--- a/drivers/nvmem/sunxi_sid.c
+++ b/drivers/nvmem/sunxi_sid.c
@@ -184,6 +184,11 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = {
.need_register_readout = true,
};
+static const struct sunxi_sid_cfg sun20i_d1_cfg = {
+ .value_offset = 0x200,
+ .size = 0x100,
+};
+
static const struct sunxi_sid_cfg sun50i_a64_cfg = {
.value_offset = 0x200,
.size = 0x100,
@@ -200,6 +205,7 @@ static const struct of_device_id sunxi_sid_of_match[] = {
{ .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg },
{ .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg },
{ .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg },
+ { .compatible = "allwinner,sun20i-d1-sid", .data = &sun20i_d1_cfg },
{ .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg },
{ .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg },
{ .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg },
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 793350028906..a16b74f32aa9 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -514,6 +514,7 @@ static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "qcom,smem" },
{ .compatible = "ramoops" },
{ .compatible = "nvmem-rmem" },
+ { .compatible = "google,open-dice" },
{}
};
diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig
index e78a9f0302c7..68a4fe4cd60b 100644
--- a/drivers/parport/Kconfig
+++ b/drivers/parport/Kconfig
@@ -42,7 +42,7 @@ if PARPORT
config PARPORT_PC
tristate "PC-style hardware"
- depends on ARCH_MIGHT_HAVE_PC_PARPORT
+ depends on ARCH_MIGHT_HAVE_PC_PARPORT || (PCI && !S390)
help
You should say Y here if you have a PC-style parallel port. All
IBM PC compatible computers and some Alphas have PC-style
@@ -77,7 +77,7 @@ config PARPORT_PC_FIFO
config PARPORT_PC_SUPERIO
bool "SuperIO chipset support"
- depends on PARPORT_PC && !PARISC
+ depends on ARCH_MIGHT_HAVE_PC_PARPORT && PARPORT_PC && !PARISC
help
Saying Y here enables some probes for Super-IO chipsets in order to
find out things like base addresses, IRQ lines and DMA channels. It
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index 401fb5eb7645..09d9bf465d72 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1631,9 +1631,7 @@ static int advk_pcie_enable_phy(struct advk_pcie *pcie)
}
ret = phy_power_on(pcie->phy);
- if (ret == -EOPNOTSUPP) {
- dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n");
- } else if (ret) {
+ if (ret) {
phy_exit(pcie->phy);
return ret;
}
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
new file mode 100644
index 000000000000..89872ad83320
--- /dev/null
+++ b/drivers/peci/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig PECI
+ tristate "PECI support"
+ help
+ The Platform Environment Control Interface (PECI) is an interface
+ that provides a communication channel to Intel processors and
+ chipset components from external monitoring or control devices.
+
+ If you are building a Baseboard Management Controller (BMC) kernel
+ for Intel platform say Y here and also to the specific driver for
+ your adapter(s) below. If unsure say N.
+
+ This support is also available as a module. If so, the module
+ will be called peci.
+
+if PECI
+
+config PECI_CPU
+ tristate "PECI CPU"
+ select AUXILIARY_BUS
+ help
+ This option enables peci-cpu driver for Intel processors. It is
+ responsible for creating auxiliary devices that can subsequently
+ be used by other drivers in order to perform various
+ functionalities such as e.g. temperature monitoring.
+
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-cpu.
+
+source "drivers/peci/controller/Kconfig"
+
+endif # PECI
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
new file mode 100644
index 000000000000..7de18137e738
--- /dev/null
+++ b/drivers/peci/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Core functionality
+peci-y := core.o request.o device.o sysfs.o
+obj-$(CONFIG_PECI) += peci.o
+peci-cpu-y := cpu.o
+obj-$(CONFIG_PECI_CPU) += peci-cpu.o
+
+# Hardware specific bus drivers
+obj-y += controller/
diff --git a/drivers/peci/controller/Kconfig b/drivers/peci/controller/Kconfig
new file mode 100644
index 000000000000..2fc5e2abb74a
--- /dev/null
+++ b/drivers/peci/controller/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config PECI_ASPEED
+ tristate "ASPEED PECI support"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on OF
+ depends on HAS_IOMEM
+ depends on COMMON_CLK
+ help
+ This option enables PECI controller driver for ASPEED AST2400,
+ AST2500 and AST2600 SoCs. It allows BMC to discover devices
+ connected to it, and communicate with them using PECI protocol.
+
+ Say Y here if your system runs on ASPEED SoC and you are using it
+ as BMC for Intel platform.
+
+ This driver can also be built as a module. If so, the module will
+ be called peci-aspeed.
diff --git a/drivers/peci/controller/Makefile b/drivers/peci/controller/Makefile
new file mode 100644
index 000000000000..022c28ef1bf0
--- /dev/null
+++ b/drivers/peci/controller/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
diff --git a/drivers/peci/controller/peci-aspeed.c b/drivers/peci/controller/peci-aspeed.c
new file mode 100644
index 000000000000..1925ddc13f00
--- /dev/null
+++ b/drivers/peci/controller/peci-aspeed.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018-2021 Intel Corporation
+
+#include <asm/unaligned.h>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+/* Control Register */
+#define ASPEED_PECI_CTRL 0x00
+#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
+#define ASPEED_PECI_CTRL_RD_MODE_MASK GENMASK(13, 12)
+#define ASPEED_PECI_CTRL_RD_MODE_DBG BIT(13)
+#define ASPEED_PECI_CTRL_RD_MODE_COUNT BIT(12)
+#define ASPEED_PECI_CTRL_CLK_SRC_HCLK BIT(11)
+#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
+#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7)
+#define ASPEED_PECI_CTRL_INVERT_IN BIT(6)
+#define ASPEED_PECI_CTRL_BUS_CONTENTION_EN BIT(5)
+#define ASPEED_PECI_CTRL_PECI_EN BIT(4)
+#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0)
+
+/* Timing Negotiation Register */
+#define ASPEED_PECI_TIMING_NEGOTIATION 0x04
+#define ASPEED_PECI_T_NEGO_MSG_MASK GENMASK(15, 8)
+#define ASPEED_PECI_T_NEGO_ADDR_MASK GENMASK(7, 0)
+
+/* Command Register */
+#define ASPEED_PECI_CMD 0x08
+#define ASPEED_PECI_CMD_PIN_MONITORING BIT(31)
+#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24)
+#define ASPEED_PECI_CMD_STS_ADDR_T_NEGO 0x3
+#define ASPEED_PECI_CMD_IDLE_MASK \
+ (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MONITORING)
+#define ASPEED_PECI_CMD_FIRE BIT(0)
+
+/* Read/Write Length Register */
+#define ASPEED_PECI_RW_LENGTH 0x0c
+#define ASPEED_PECI_AW_FCS_EN BIT(31)
+#define ASPEED_PECI_RD_LEN_MASK GENMASK(23, 16)
+#define ASPEED_PECI_WR_LEN_MASK GENMASK(15, 8)
+#define ASPEED_PECI_TARGET_ADDR_MASK GENMASK(7, 0)
+
+/* Expected FCS Data Register */
+#define ASPEED_PECI_EXPECTED_FCS 0x10
+#define ASPEED_PECI_EXPECTED_RD_FCS_MASK GENMASK(23, 16)
+#define ASPEED_PECI_EXPECTED_AW_FCS_AUTO_MASK GENMASK(15, 8)
+#define ASPEED_PECI_EXPECTED_WR_FCS_MASK GENMASK(7, 0)
+
+/* Captured FCS Data Register */
+#define ASPEED_PECI_CAPTURED_FCS 0x14
+#define ASPEED_PECI_CAPTURED_RD_FCS_MASK GENMASK(23, 16)
+#define ASPEED_PECI_CAPTURED_WR_FCS_MASK GENMASK(7, 0)
+
+/* Interrupt Register */
+#define ASPEED_PECI_INT_CTRL 0x18
+#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30)
+#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0
+#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1
+#define ASPEED_PECI_MESSAGE_NEGO 2
+#define ASPEED_PECI_INT_MASK GENMASK(4, 0)
+#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4)
+#define ASPEED_PECI_INT_BUS_CONTENTION BIT(3)
+#define ASPEED_PECI_INT_WR_FCS_BAD BIT(2)
+#define ASPEED_PECI_INT_WR_FCS_ABORT BIT(1)
+#define ASPEED_PECI_INT_CMD_DONE BIT(0)
+
+/* Interrupt Status Register */
+#define ASPEED_PECI_INT_STS 0x1c
+#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16)
+ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
+
+/* Rx/Tx Data Buffer Registers */
+#define ASPEED_PECI_WR_DATA0 0x20
+#define ASPEED_PECI_WR_DATA1 0x24
+#define ASPEED_PECI_WR_DATA2 0x28
+#define ASPEED_PECI_WR_DATA3 0x2c
+#define ASPEED_PECI_RD_DATA0 0x30
+#define ASPEED_PECI_RD_DATA1 0x34
+#define ASPEED_PECI_RD_DATA2 0x38
+#define ASPEED_PECI_RD_DATA3 0x3c
+#define ASPEED_PECI_WR_DATA4 0x40
+#define ASPEED_PECI_WR_DATA5 0x44
+#define ASPEED_PECI_WR_DATA6 0x48
+#define ASPEED_PECI_WR_DATA7 0x4c
+#define ASPEED_PECI_RD_DATA4 0x50
+#define ASPEED_PECI_RD_DATA5 0x54
+#define ASPEED_PECI_RD_DATA6 0x58
+#define ASPEED_PECI_RD_DATA7 0x5c
+#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32
+
+/* Timing Negotiation */
+#define ASPEED_PECI_CLK_FREQUENCY_MIN 2000
+#define ASPEED_PECI_CLK_FREQUENCY_DEFAULT 1000000
+#define ASPEED_PECI_CLK_FREQUENCY_MAX 2000000
+#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8
+/* Timeout */
+#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_US (50 * USEC_PER_MSEC)
+#define ASPEED_PECI_IDLE_CHECK_INTERVAL_US (10 * USEC_PER_MSEC)
+#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 1000
+
+#define ASPEED_PECI_CLK_DIV1(msg_timing) (4 * (msg_timing) + 1)
+#define ASPEED_PECI_CLK_DIV2(clk_div_exp) BIT(clk_div_exp)
+#define ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp) \
+ (4 * ASPEED_PECI_CLK_DIV1(msg_timing) * ASPEED_PECI_CLK_DIV2(clk_div_exp))
+
+struct aspeed_peci {
+ struct peci_controller *controller;
+ struct device *dev;
+ void __iomem *base;
+ struct reset_control *rst;
+ int irq;
+ spinlock_t lock; /* to sync completion status handling */
+ struct completion xfer_complete;
+ struct clk *clk;
+ u32 clk_frequency;
+ u32 status;
+ u32 cmd_timeout_ms;
+};
+
+struct clk_aspeed_peci {
+ struct clk_hw hw;
+ struct aspeed_peci *aspeed_peci;
+};
+
+static void aspeed_peci_controller_enable(struct aspeed_peci *priv)
+{
+ u32 val = readl(priv->base + ASPEED_PECI_CTRL);
+
+ val |= ASPEED_PECI_CTRL_PECI_CLK_EN;
+ val |= ASPEED_PECI_CTRL_PECI_EN;
+
+ writel(val, priv->base + ASPEED_PECI_CTRL);
+}
+
+static void aspeed_peci_init_regs(struct aspeed_peci *priv)
+{
+ u32 val;
+
+ /* Clear interrupts */
+ writel(ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_STS);
+
+ /* Set timing negotiation mode and enable interrupts */
+ val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO);
+ val |= ASPEED_PECI_INT_MASK;
+ writel(val, priv->base + ASPEED_PECI_INT_CTRL);
+
+ val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
+ writel(val, priv->base + ASPEED_PECI_CTRL);
+}
+
+static int aspeed_peci_check_idle(struct aspeed_peci *priv)
+{
+ u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
+ int ret;
+
+ /*
+ * Under normal circumstances, we expect to be idle here.
+ * In case there were any errors/timeouts that led to the situation
+ * where the hardware is not in idle state - we need to reset and
+ * reinitialize it to avoid potential controller hang.
+ */
+ if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts)) {
+ ret = reset_control_assert(priv->rst);
+ if (ret) {
+ dev_err(priv->dev, "cannot assert reset control\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(priv->rst);
+ if (ret) {
+ dev_err(priv->dev, "cannot deassert reset control\n");
+ return ret;
+ }
+
+ aspeed_peci_init_regs(priv);
+
+ ret = clk_set_rate(priv->clk, priv->clk_frequency);
+ if (ret < 0) {
+ dev_err(priv->dev, "cannot set clock frequency\n");
+ return ret;
+ }
+
+ aspeed_peci_controller_enable(priv);
+ }
+
+ return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
+ cmd_sts,
+ !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
+ ASPEED_PECI_IDLE_CHECK_INTERVAL_US,
+ ASPEED_PECI_IDLE_CHECK_TIMEOUT_US);
+}
+
+static int aspeed_peci_xfer(struct peci_controller *controller,
+ u8 addr, struct peci_request *req)
+{
+ struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent);
+ unsigned long timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+ u32 peci_head;
+ int ret, i;
+
+ if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
+ req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
+ return -EINVAL;
+
+ /* Check command sts and bus idle state */
+ ret = aspeed_peci_check_idle(priv);
+ if (ret)
+ return ret; /* -ETIMEDOUT */
+
+ spin_lock_irq(&priv->lock);
+ reinit_completion(&priv->xfer_complete);
+
+ peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) |
+ FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) |
+ FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len);
+
+ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
+
+ for (i = 0; i < req->tx.len; i += 4) {
+ u32 reg = (i < 16 ? ASPEED_PECI_WR_DATA0 : ASPEED_PECI_WR_DATA4) + i % 16;
+
+ writel(get_unaligned_le32(&req->tx.buf[i]), priv->base + reg);
+ }
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
+ dev_dbg(priv->dev, "HEAD : %#08x\n", peci_head);
+ print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len);
+#endif
+
+ priv->status = 0;
+ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
+ spin_unlock_irq(&priv->lock);
+
+ ret = wait_for_completion_interruptible_timeout(&priv->xfer_complete, timeout);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0) {
+ dev_dbg(priv->dev, "timeout waiting for a response\n");
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_irq(&priv->lock);
+
+ if (priv->status != ASPEED_PECI_INT_CMD_DONE) {
+ spin_unlock_irq(&priv->lock);
+ dev_dbg(priv->dev, "no valid response, status: %#02x\n", priv->status);
+ return -EIO;
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ /*
+ * We need to use dword reads for register access, make sure that the
+ * buffer size is multiple of 4-bytes.
+ */
+ BUILD_BUG_ON(PECI_REQUEST_MAX_BUF_SIZE % 4);
+
+ for (i = 0; i < req->rx.len; i += 4) {
+ u32 reg = (i < 16 ? ASPEED_PECI_RD_DATA0 : ASPEED_PECI_RD_DATA4) + i % 16;
+ u32 rx_data = readl(priv->base + reg);
+
+ put_unaligned_le32(rx_data, &req->rx.buf[i]);
+ }
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
+ print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req->rx.len);
+#endif
+ return 0;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+ struct aspeed_peci *priv = arg;
+ u32 status;
+
+ spin_lock(&priv->lock);
+ status = readl(priv->base + ASPEED_PECI_INT_STS);
+ writel(status, priv->base + ASPEED_PECI_INT_STS);
+ priv->status |= (status & ASPEED_PECI_INT_MASK);
+
+ /*
+ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
+ * set even in an error case.
+ */
+ if (status & ASPEED_PECI_INT_CMD_DONE)
+ complete(&priv->xfer_complete);
+
+ writel(0, priv->base + ASPEED_PECI_CMD);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void clk_aspeed_peci_find_div_values(unsigned long rate, int *msg_timing, int *clk_div_exp)
+{
+ unsigned long best_diff = ~0ul, diff;
+ int msg_timing_temp, clk_div_exp_temp, i, j;
+
+ for (i = 1; i <= 255; i++)
+ for (j = 0; j < 8; j++) {
+ diff = abs(rate - ASPEED_PECI_CLK_DIV1(i) * ASPEED_PECI_CLK_DIV2(j));
+ if (diff < best_diff) {
+ msg_timing_temp = i;
+ clk_div_exp_temp = j;
+ best_diff = diff;
+ }
+ }
+
+ *msg_timing = msg_timing_temp;
+ *clk_div_exp = clk_div_exp_temp;
+}
+
+static int clk_aspeed_peci_get_div(unsigned long rate, const unsigned long *prate)
+{
+ unsigned long this_rate = *prate / (4 * rate);
+ int msg_timing, clk_div_exp;
+
+ clk_aspeed_peci_find_div_values(this_rate, &msg_timing, &clk_div_exp);
+
+ return ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp);
+}
+
+static int clk_aspeed_peci_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct clk_aspeed_peci *peci_clk = container_of(hw, struct clk_aspeed_peci, hw);
+ struct aspeed_peci *aspeed_peci = peci_clk->aspeed_peci;
+ unsigned long this_rate = prate / (4 * rate);
+ int clk_div_exp, msg_timing;
+ u32 val;
+
+ clk_aspeed_peci_find_div_values(this_rate, &msg_timing, &clk_div_exp);
+
+ val = readl(aspeed_peci->base + ASPEED_PECI_CTRL);
+ val |= FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_exp);
+ writel(val, aspeed_peci->base + ASPEED_PECI_CTRL);
+
+ val = FIELD_PREP(ASPEED_PECI_T_NEGO_MSG_MASK, msg_timing);
+ val |= FIELD_PREP(ASPEED_PECI_T_NEGO_ADDR_MASK, msg_timing);
+ writel(val, aspeed_peci->base + ASPEED_PECI_TIMING_NEGOTIATION);
+
+ return 0;
+}
+
+static long clk_aspeed_peci_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int div = clk_aspeed_peci_get_div(rate, prate);
+
+ return DIV_ROUND_UP_ULL(*prate, div);
+}
+
+static unsigned long clk_aspeed_peci_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+ struct clk_aspeed_peci *peci_clk = container_of(hw, struct clk_aspeed_peci, hw);
+ struct aspeed_peci *aspeed_peci = peci_clk->aspeed_peci;
+ int div, msg_timing, addr_timing, clk_div_exp;
+ u32 reg;
+
+ reg = readl(aspeed_peci->base + ASPEED_PECI_TIMING_NEGOTIATION);
+ msg_timing = FIELD_GET(ASPEED_PECI_T_NEGO_MSG_MASK, reg);
+ addr_timing = FIELD_GET(ASPEED_PECI_T_NEGO_ADDR_MASK, reg);
+
+ if (msg_timing != addr_timing)
+ return 0;
+
+ reg = readl(aspeed_peci->base + ASPEED_PECI_CTRL);
+ clk_div_exp = FIELD_GET(ASPEED_PECI_CTRL_CLK_DIV_MASK, reg);
+
+ div = ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp);
+
+ return DIV_ROUND_UP_ULL(prate, div);
+}
+
+static const struct clk_ops clk_aspeed_peci_ops = {
+ .set_rate = clk_aspeed_peci_set_rate,
+ .round_rate = clk_aspeed_peci_round_rate,
+ .recalc_rate = clk_aspeed_peci_recalc_rate,
+};
+
+/*
+ * PECI HW contains a clock divider which is a combination of:
+ * div0: 4 (fixed divider)
+ * div1: x + 1
+ * div2: 1 << y
+ * In other words, out_clk = in_clk / (div0 * div1 * div2)
+ * The resulting frequency is used by PECI Controller to drive the PECI bus to
+ * negotiate optimal transfer rate.
+ */
+static struct clk *devm_aspeed_peci_register_clk_div(struct device *dev, struct clk *parent,
+ struct aspeed_peci *priv)
+{
+ struct clk_aspeed_peci *peci_clk;
+ struct clk_init_data init;
+ const char *parent_name;
+ char name[32];
+ int ret;
+
+ snprintf(name, sizeof(name), "%s_div", dev_name(dev));
+
+ parent_name = __clk_get_name(parent);
+
+ init.ops = &clk_aspeed_peci_ops;
+ init.name = name;
+ init.parent_names = (const char* []) { parent_name };
+ init.num_parents = 1;
+ init.flags = 0;
+
+ peci_clk = devm_kzalloc(dev, sizeof(struct clk_aspeed_peci), GFP_KERNEL);
+ if (!peci_clk)
+ return ERR_PTR(-ENOMEM);
+
+ peci_clk->hw.init = &init;
+ peci_clk->aspeed_peci = priv;
+
+ ret = devm_clk_hw_register(dev, &peci_clk->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return peci_clk->hw.clk;
+}
+
+static void aspeed_peci_property_sanitize(struct device *dev, const char *propname,
+ u32 min, u32 max, u32 default_val, u32 *propval)
+{
+ u32 val;
+ int ret;
+
+ ret = device_property_read_u32(dev, propname, &val);
+ if (ret) {
+ val = default_val;
+ } else if (val > max || val < min) {
+ dev_warn(dev, "invalid %s: %u, falling back to: %u\n",
+ propname, val, default_val);
+
+ val = default_val;
+ }
+
+ *propval = val;
+}
+
+static void aspeed_peci_property_setup(struct aspeed_peci *priv)
+{
+ aspeed_peci_property_sanitize(priv->dev, "clock-frequency",
+ ASPEED_PECI_CLK_FREQUENCY_MIN, ASPEED_PECI_CLK_FREQUENCY_MAX,
+ ASPEED_PECI_CLK_FREQUENCY_DEFAULT, &priv->clk_frequency);
+ aspeed_peci_property_sanitize(priv->dev, "cmd-timeout-ms",
+ 1, ASPEED_PECI_CMD_TIMEOUT_MS_MAX,
+ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, &priv->cmd_timeout_ms);
+}
+
+static struct peci_controller_ops aspeed_ops = {
+ .xfer = aspeed_peci_xfer,
+};
+
+static void aspeed_peci_reset_control_release(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int devm_aspeed_peci_reset_control_deassert(struct device *dev, struct reset_control *rst)
+{
+ int ret;
+
+ ret = reset_control_deassert(rst);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, aspeed_peci_reset_control_release, rst);
+}
+
+static void aspeed_peci_clk_release(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static int devm_aspeed_peci_clk_enable(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, aspeed_peci_clk_release, clk);
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+ struct peci_controller *controller;
+ struct aspeed_peci *priv;
+ struct clk *ref_clk;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(priv->dev, priv);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (!priv->irq)
+ return priv->irq;
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+ 0, "peci-aspeed", priv);
+ if (ret)
+ return ret;
+
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
+ priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->rst))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->rst),
+ "failed to get reset control\n");
+
+ ret = devm_aspeed_peci_reset_control_deassert(priv->dev, priv->rst);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "cannot deassert reset control\n");
+
+ aspeed_peci_property_setup(priv);
+
+ aspeed_peci_init_regs(priv);
+
+ ref_clk = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(ref_clk))
+ return dev_err_probe(priv->dev, PTR_ERR(ref_clk), "failed to get ref clock\n");
+
+ priv->clk = devm_aspeed_peci_register_clk_div(priv->dev, ref_clk, priv);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->clk), "cannot register clock\n");
+
+ ret = clk_set_rate(priv->clk, priv->clk_frequency);
+ if (ret < 0)
+ return dev_err_probe(priv->dev, ret, "cannot set clock frequency\n");
+
+ ret = devm_aspeed_peci_clk_enable(priv->dev, priv->clk);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "failed to enable clock\n");
+
+ aspeed_peci_controller_enable(priv);
+
+ controller = devm_peci_controller_add(priv->dev, &aspeed_ops);
+ if (IS_ERR(controller))
+ return dev_err_probe(priv->dev, PTR_ERR(controller),
+ "failed to add aspeed peci controller\n");
+
+ priv->controller = controller;
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+ { .compatible = "aspeed,ast2400-peci", },
+ { .compatible = "aspeed,ast2500-peci", },
+ { .compatible = "aspeed,ast2600-peci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+ .probe = aspeed_peci_probe,
+ .driver = {
+ .name = "peci-aspeed",
+ .of_match_table = aspeed_peci_of_table,
+ },
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PECI);
diff --git a/drivers/peci/core.c b/drivers/peci/core.c
new file mode 100644
index 000000000000..9c8cf07e51c7
--- /dev/null
+++ b/drivers/peci/core.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2021 Intel Corporation
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+#include "internal.h"
+
+static DEFINE_IDA(peci_controller_ida);
+
+static void peci_controller_dev_release(struct device *dev)
+{
+ struct peci_controller *controller = to_peci_controller(dev);
+
+ mutex_destroy(&controller->bus_lock);
+ ida_free(&peci_controller_ida, controller->id);
+ kfree(controller);
+}
+
+struct device_type peci_controller_type = {
+ .release = peci_controller_dev_release,
+};
+
+int peci_controller_scan_devices(struct peci_controller *controller)
+{
+ int ret;
+ u8 addr;
+
+ for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) {
+ ret = peci_device_create(controller, addr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct peci_controller *peci_controller_alloc(struct device *dev,
+ struct peci_controller_ops *ops)
+{
+ struct peci_controller *controller;
+ int ret;
+
+ if (!ops->xfer)
+ return ERR_PTR(-EINVAL);
+
+ controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return ERR_PTR(-ENOMEM);
+
+ ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL);
+ if (ret < 0)
+ goto err;
+ controller->id = ret;
+
+ controller->ops = ops;
+
+ controller->dev.parent = dev;
+ controller->dev.bus = &peci_bus_type;
+ controller->dev.type = &peci_controller_type;
+
+ device_initialize(&controller->dev);
+
+ mutex_init(&controller->bus_lock);
+
+ return controller;
+
+err:
+ kfree(controller);
+ return ERR_PTR(ret);
+}
+
+static int unregister_child(struct device *dev, void *dummy)
+{
+ peci_device_destroy(to_peci_device(dev));
+
+ return 0;
+}
+
+static void unregister_controller(void *_controller)
+{
+ struct peci_controller *controller = _controller;
+
+ /*
+ * Detach any active PECI devices. This can't fail, thus we do not
+ * check the returned value.
+ */
+ device_for_each_child_reverse(&controller->dev, NULL, unregister_child);
+
+ device_unregister(&controller->dev);
+
+ fwnode_handle_put(controller->dev.fwnode);
+
+ pm_runtime_disable(&controller->dev);
+}
+
+/**
+ * devm_peci_controller_add() - add PECI controller
+ * @dev: device for devm operations
+ * @ops: pointer to controller specific methods
+ *
+ * In final stage of its probe(), peci_controller driver calls
+ * devm_peci_controller_add() to register itself with the PECI bus.
+ *
+ * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure.
+ */
+struct peci_controller *devm_peci_controller_add(struct device *dev,
+ struct peci_controller_ops *ops)
+{
+ struct peci_controller *controller;
+ int ret;
+
+ controller = peci_controller_alloc(dev, ops);
+ if (IS_ERR(controller))
+ return controller;
+
+ ret = dev_set_name(&controller->dev, "peci-%d", controller->id);
+ if (ret)
+ goto err_put;
+
+ pm_runtime_no_callbacks(&controller->dev);
+ pm_suspend_ignore_children(&controller->dev, true);
+ pm_runtime_enable(&controller->dev);
+
+ device_set_node(&controller->dev, fwnode_handle_get(dev_fwnode(dev)));
+
+ ret = device_add(&controller->dev);
+ if (ret)
+ goto err_fwnode;
+
+ ret = devm_add_action_or_reset(dev, unregister_controller, controller);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /*
+ * Ignoring retval since failures during scan are non-critical for
+ * controller itself.
+ */
+ peci_controller_scan_devices(controller);
+
+ return controller;
+
+err_fwnode:
+ fwnode_handle_put(controller->dev.fwnode);
+
+ pm_runtime_disable(&controller->dev);
+
+err_put:
+ put_device(&controller->dev);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI);
+
+static const struct peci_device_id *
+peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device)
+{
+ while (id->family != 0) {
+ if (id->family == device->info.family &&
+ id->model == device->info.model)
+ return id;
+ id++;
+ }
+
+ return NULL;
+}
+
+static int peci_bus_device_match(struct device *dev, struct device_driver *drv)
+{
+ struct peci_device *device = to_peci_device(dev);
+ struct peci_driver *peci_drv = to_peci_driver(drv);
+
+ if (dev->type != &peci_device_type)
+ return 0;
+
+ return !!peci_bus_match_device_id(peci_drv->id_table, device);
+}
+
+static int peci_bus_device_probe(struct device *dev)
+{
+ struct peci_device *device = to_peci_device(dev);
+ struct peci_driver *driver = to_peci_driver(dev->driver);
+
+ return driver->probe(device, peci_bus_match_device_id(driver->id_table, device));
+}
+
+static void peci_bus_device_remove(struct device *dev)
+{
+ struct peci_device *device = to_peci_device(dev);
+ struct peci_driver *driver = to_peci_driver(dev->driver);
+
+ if (driver->remove)
+ driver->remove(device);
+}
+
+struct bus_type peci_bus_type = {
+ .name = "peci",
+ .match = peci_bus_device_match,
+ .probe = peci_bus_device_probe,
+ .remove = peci_bus_device_remove,
+ .bus_groups = peci_bus_groups,
+};
+
+static int __init peci_init(void)
+{
+ int ret;
+
+ ret = bus_register(&peci_bus_type);
+ if (ret < 0) {
+ pr_err("peci: failed to register PECI bus type!\n");
+ return ret;
+ }
+
+ return 0;
+}
+module_init(peci_init);
+
+static void __exit peci_exit(void)
+{
+ bus_unregister(&peci_bus_type);
+}
+module_exit(peci_exit);
+
+MODULE_AUTHOR("Jason M Bills <jason.m.bills@linux.intel.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
+MODULE_DESCRIPTION("PECI bus core module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/peci/cpu.c b/drivers/peci/cpu.c
new file mode 100644
index 000000000000..68eb61c65d34
--- /dev/null
+++ b/drivers/peci/cpu.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2021 Intel Corporation
+
+#include <linux/auxiliary_bus.h>
+#include <linux/module.h>
+#include <linux/peci.h>
+#include <linux/peci-cpu.h>
+#include <linux/slab.h>
+
+#include "internal.h"
+
+/**
+ * peci_temp_read() - read the maximum die temperature from PECI target device
+ * @device: PECI device to which request is going to be sent
+ * @temp_raw: where to store the read temperature
+ *
+ * It uses GetTemp PECI command.
+ *
+ * Return: 0 if succeeded, other values in case errors.
+ */
+int peci_temp_read(struct peci_device *device, s16 *temp_raw)
+{
+ struct peci_request *req;
+
+ req = peci_xfer_get_temp(device);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ *temp_raw = peci_request_temp_read(req);
+
+ peci_request_free(req);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(peci_temp_read, PECI_CPU);
+
+/**
+ * peci_pcs_read() - read PCS register
+ * @device: PECI device to which request is going to be sent
+ * @index: PCS index
+ * @param: PCS parameter
+ * @data: where to store the read data
+ *
+ * It uses RdPkgConfig PECI command.
+ *
+ * Return: 0 if succeeded, other values in case errors.
+ */
+int peci_pcs_read(struct peci_device *device, u8 index, u16 param, u32 *data)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_xfer_pkg_cfg_readl(device, index, param);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = peci_request_status(req);
+ if (ret)
+ goto out_req_free;
+
+ *data = peci_request_data_readl(req);
+out_req_free:
+ peci_request_free(req);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(peci_pcs_read, PECI_CPU);
+
+/**
+ * peci_pci_local_read() - read 32-bit memory location using raw address
+ * @device: PECI device to which request is going to be sent
+ * @bus: bus
+ * @dev: device
+ * @func: function
+ * @reg: register
+ * @data: where to store the read data
+ *
+ * It uses RdPCIConfigLocal PECI command.
+ *
+ * Return: 0 if succeeded, other values in case errors.
+ */
+int peci_pci_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func,
+ u16 reg, u32 *data)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_xfer_pci_cfg_local_readl(device, bus, dev, func, reg);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = peci_request_status(req);
+ if (ret)
+ goto out_req_free;
+
+ *data = peci_request_data_readl(req);
+out_req_free:
+ peci_request_free(req);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(peci_pci_local_read, PECI_CPU);
+
+/**
+ * peci_ep_pci_local_read() - read 32-bit memory location using raw address
+ * @device: PECI device to which request is going to be sent
+ * @seg: PCI segment
+ * @bus: bus
+ * @dev: device
+ * @func: function
+ * @reg: register
+ * @data: where to store the read data
+ *
+ * Like &peci_pci_local_read, but it uses RdEndpointConfig PECI command.
+ *
+ * Return: 0 if succeeded, other values in case errors.
+ */
+int peci_ep_pci_local_read(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg, u32 *data)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_xfer_ep_pci_cfg_local_readl(device, seg, bus, dev, func, reg);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = peci_request_status(req);
+ if (ret)
+ goto out_req_free;
+
+ *data = peci_request_data_readl(req);
+out_req_free:
+ peci_request_free(req);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(peci_ep_pci_local_read, PECI_CPU);
+
+/**
+ * peci_mmio_read() - read 32-bit memory location using 64-bit bar offset address
+ * @device: PECI device to which request is going to be sent
+ * @bar: PCI bar
+ * @seg: PCI segment
+ * @bus: bus
+ * @dev: device
+ * @func: function
+ * @address: 64-bit MMIO address
+ * @data: where to store the read data
+ *
+ * It uses RdEndpointConfig PECI command.
+ *
+ * Return: 0 if succeeded, other values in case errors.
+ */
+int peci_mmio_read(struct peci_device *device, u8 bar, u8 seg,
+ u8 bus, u8 dev, u8 func, u64 address, u32 *data)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_xfer_ep_mmio64_readl(device, bar, seg, bus, dev, func, address);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = peci_request_status(req);
+ if (ret)
+ goto out_req_free;
+
+ *data = peci_request_data_readl(req);
+out_req_free:
+ peci_request_free(req);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(peci_mmio_read, PECI_CPU);
+
+static const char * const peci_adev_types[] = {
+ "cputemp",
+ "dimmtemp",
+};
+
+struct peci_cpu {
+ struct peci_device *device;
+ const struct peci_device_id *id;
+};
+
+static void adev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ auxiliary_device_uninit(adev);
+
+ kfree(adev->name);
+ kfree(adev);
+}
+
+static struct auxiliary_device *adev_alloc(struct peci_cpu *priv, int idx)
+{
+ struct peci_controller *controller = to_peci_controller(priv->device->dev.parent);
+ struct auxiliary_device *adev;
+ const char *name;
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return ERR_PTR(-ENOMEM);
+
+ name = kasprintf(GFP_KERNEL, "%s.%s", peci_adev_types[idx], (const char *)priv->id->data);
+ if (!name) {
+ ret = -ENOMEM;
+ goto free_adev;
+ }
+
+ adev->name = name;
+ adev->dev.parent = &priv->device->dev;
+ adev->dev.release = adev_release;
+ adev->id = (controller->id << 16) | (priv->device->addr);
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ goto free_name;
+
+ return adev;
+
+free_name:
+ kfree(name);
+free_adev:
+ kfree(adev);
+ return ERR_PTR(ret);
+}
+
+static void unregister_adev(void *_adev)
+{
+ struct auxiliary_device *adev = _adev;
+
+ auxiliary_device_delete(adev);
+}
+
+static int devm_adev_add(struct device *dev, int idx)
+{
+ struct peci_cpu *priv = dev_get_drvdata(dev);
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = adev_alloc(priv, idx);
+ if (IS_ERR(adev))
+ return PTR_ERR(adev);
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&priv->device->dev, unregister_adev, adev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void peci_cpu_add_adevices(struct peci_cpu *priv)
+{
+ struct device *dev = &priv->device->dev;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(peci_adev_types); i++) {
+ ret = devm_adev_add(dev, i);
+ if (ret) {
+ dev_warn(dev, "Failed to register PECI auxiliary: %s, ret = %d\n",
+ peci_adev_types[i], ret);
+ continue;
+ }
+ }
+}
+
+static int
+peci_cpu_probe(struct peci_device *device, const struct peci_device_id *id)
+{
+ struct device *dev = &device->dev;
+ struct peci_cpu *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->device = device;
+ priv->id = id;
+
+ peci_cpu_add_adevices(priv);
+
+ return 0;
+}
+
+static const struct peci_device_id peci_cpu_device_ids[] = {
+ { /* Haswell Xeon */
+ .family = 6,
+ .model = INTEL_FAM6_HASWELL_X,
+ .data = "hsx",
+ },
+ { /* Broadwell Xeon */
+ .family = 6,
+ .model = INTEL_FAM6_BROADWELL_X,
+ .data = "bdx",
+ },
+ { /* Broadwell Xeon D */
+ .family = 6,
+ .model = INTEL_FAM6_BROADWELL_D,
+ .data = "bdxd",
+ },
+ { /* Skylake Xeon */
+ .family = 6,
+ .model = INTEL_FAM6_SKYLAKE_X,
+ .data = "skx",
+ },
+ { /* Icelake Xeon */
+ .family = 6,
+ .model = INTEL_FAM6_ICELAKE_X,
+ .data = "icx",
+ },
+ { /* Icelake Xeon D */
+ .family = 6,
+ .model = INTEL_FAM6_ICELAKE_D,
+ .data = "icxd",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(peci, peci_cpu_device_ids);
+
+static struct peci_driver peci_cpu_driver = {
+ .probe = peci_cpu_probe,
+ .id_table = peci_cpu_device_ids,
+ .driver = {
+ .name = "peci-cpu",
+ },
+};
+module_peci_driver(peci_cpu_driver);
+
+MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
+MODULE_DESCRIPTION("PECI CPU driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PECI);
diff --git a/drivers/peci/device.c b/drivers/peci/device.c
new file mode 100644
index 000000000000..e6b0bffb14f4
--- /dev/null
+++ b/drivers/peci/device.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2021 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/peci.h>
+#include <linux/peci-cpu.h>
+#include <linux/slab.h>
+
+#include "internal.h"
+
+/*
+ * PECI device can be removed using sysfs, but the removal can also happen as
+ * a result of controller being removed.
+ * Mutex is used to protect PECI device from being double-deleted.
+ */
+static DEFINE_MUTEX(peci_device_del_lock);
+
+#define REVISION_NUM_MASK GENMASK(15, 8)
+static int peci_get_revision(struct peci_device *device, u8 *revision)
+{
+ struct peci_request *req;
+ u64 dib;
+
+ req = peci_xfer_get_dib(device);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ /*
+ * PECI device may be in a state where it is unable to return a proper
+ * DIB, in which case it returns 0 as DIB value.
+ * Let's treat this as an error to avoid carrying on with the detection
+ * using invalid revision.
+ */
+ dib = peci_request_dib_read(req);
+ if (dib == 0) {
+ peci_request_free(req);
+ return -EIO;
+ }
+
+ *revision = FIELD_GET(REVISION_NUM_MASK, dib);
+
+ peci_request_free(req);
+
+ return 0;
+}
+
+static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_xfer_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = peci_request_status(req);
+ if (ret)
+ goto out_req_free;
+
+ *cpu_id = peci_request_data_readl(req);
+out_req_free:
+ peci_request_free(req);
+
+ return ret;
+}
+
+static unsigned int peci_x86_cpu_family(unsigned int sig)
+{
+ unsigned int x86;
+
+ x86 = (sig >> 8) & 0xf;
+
+ if (x86 == 0xf)
+ x86 += (sig >> 20) & 0xff;
+
+ return x86;
+}
+
+static unsigned int peci_x86_cpu_model(unsigned int sig)
+{
+ unsigned int fam, model;
+
+ fam = peci_x86_cpu_family(sig);
+
+ model = (sig >> 4) & 0xf;
+
+ if (fam >= 0x6)
+ model += ((sig >> 16) & 0xf) << 4;
+
+ return model;
+}
+
+static int peci_device_info_init(struct peci_device *device)
+{
+ u8 revision;
+ u32 cpu_id;
+ int ret;
+
+ ret = peci_get_cpu_id(device, &cpu_id);
+ if (ret)
+ return ret;
+
+ device->info.family = peci_x86_cpu_family(cpu_id);
+ device->info.model = peci_x86_cpu_model(cpu_id);
+
+ ret = peci_get_revision(device, &revision);
+ if (ret)
+ return ret;
+ device->info.peci_revision = revision;
+
+ device->info.socket_id = device->addr - PECI_BASE_ADDR;
+
+ return 0;
+}
+
+static int peci_detect(struct peci_controller *controller, u8 addr)
+{
+ /*
+ * PECI Ping is a command encoded by tx_len = 0, rx_len = 0.
+ * We expect correct Write FCS if the device at the target address
+ * is able to respond.
+ */
+ struct peci_request req = { 0 };
+ int ret;
+
+ mutex_lock(&controller->bus_lock);
+ ret = controller->ops->xfer(controller, addr, &req);
+ mutex_unlock(&controller->bus_lock);
+
+ return ret;
+}
+
+static bool peci_addr_valid(u8 addr)
+{
+ return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX;
+}
+
+static int peci_dev_exists(struct device *dev, void *data)
+{
+ struct peci_device *device = to_peci_device(dev);
+ u8 *addr = data;
+
+ if (device->addr == *addr)
+ return -EBUSY;
+
+ return 0;
+}
+
+int peci_device_create(struct peci_controller *controller, u8 addr)
+{
+ struct peci_device *device;
+ int ret;
+
+ if (!peci_addr_valid(addr))
+ return -EINVAL;
+
+ /* Check if we have already detected this device before. */
+ ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists);
+ if (ret)
+ return 0;
+
+ ret = peci_detect(controller, addr);
+ if (ret) {
+ /*
+ * Device not present or host state doesn't allow successful
+ * detection at this time.
+ */
+ if (ret == -EIO || ret == -ETIMEDOUT)
+ return 0;
+
+ return ret;
+ }
+
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ device_initialize(&device->dev);
+
+ device->addr = addr;
+ device->dev.parent = &controller->dev;
+ device->dev.bus = &peci_bus_type;
+ device->dev.type = &peci_device_type;
+
+ ret = peci_device_info_init(device);
+ if (ret)
+ goto err_put;
+
+ ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr);
+ if (ret)
+ goto err_put;
+
+ ret = device_add(&device->dev);
+ if (ret)
+ goto err_put;
+
+ return 0;
+
+err_put:
+ put_device(&device->dev);
+
+ return ret;
+}
+
+void peci_device_destroy(struct peci_device *device)
+{
+ mutex_lock(&peci_device_del_lock);
+ if (!device->deleted) {
+ device_unregister(&device->dev);
+ device->deleted = true;
+ }
+ mutex_unlock(&peci_device_del_lock);
+}
+
+int __peci_driver_register(struct peci_driver *driver, struct module *owner,
+ const char *mod_name)
+{
+ driver->driver.bus = &peci_bus_type;
+ driver->driver.owner = owner;
+ driver->driver.mod_name = mod_name;
+
+ if (!driver->probe) {
+ pr_err("peci: trying to register driver without probe callback\n");
+ return -EINVAL;
+ }
+
+ if (!driver->id_table) {
+ pr_err("peci: trying to register driver without device id table\n");
+ return -EINVAL;
+ }
+
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL_NS_GPL(__peci_driver_register, PECI);
+
+void peci_driver_unregister(struct peci_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, PECI);
+
+static void peci_device_release(struct device *dev)
+{
+ struct peci_device *device = to_peci_device(dev);
+
+ kfree(device);
+}
+
+struct device_type peci_device_type = {
+ .groups = peci_device_groups,
+ .release = peci_device_release,
+};
diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h
new file mode 100644
index 000000000000..9d75ea54504c
--- /dev/null
+++ b/drivers/peci/internal.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2018-2021 Intel Corporation */
+
+#ifndef __PECI_INTERNAL_H
+#define __PECI_INTERNAL_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+struct peci_controller;
+struct attribute_group;
+struct peci_device;
+struct peci_request;
+
+/* PECI CPU address range 0x30-0x37 */
+#define PECI_BASE_ADDR 0x30
+#define PECI_DEVICE_NUM_MAX 8
+
+struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len);
+void peci_request_free(struct peci_request *req);
+
+int peci_request_status(struct peci_request *req);
+
+u64 peci_request_dib_read(struct peci_request *req);
+s16 peci_request_temp_read(struct peci_request *req);
+
+u8 peci_request_data_readb(struct peci_request *req);
+u16 peci_request_data_readw(struct peci_request *req);
+u32 peci_request_data_readl(struct peci_request *req);
+u64 peci_request_data_readq(struct peci_request *req);
+
+struct peci_request *peci_xfer_get_dib(struct peci_device *device);
+struct peci_request *peci_xfer_get_temp(struct peci_device *device);
+
+struct peci_request *peci_xfer_pkg_cfg_readb(struct peci_device *device, u8 index, u16 param);
+struct peci_request *peci_xfer_pkg_cfg_readw(struct peci_device *device, u8 index, u16 param);
+struct peci_request *peci_xfer_pkg_cfg_readl(struct peci_device *device, u8 index, u16 param);
+struct peci_request *peci_xfer_pkg_cfg_readq(struct peci_device *device, u8 index, u16 param);
+
+struct peci_request *peci_xfer_pci_cfg_local_readb(struct peci_device *device,
+ u8 bus, u8 dev, u8 func, u16 reg);
+struct peci_request *peci_xfer_pci_cfg_local_readw(struct peci_device *device,
+ u8 bus, u8 dev, u8 func, u16 reg);
+struct peci_request *peci_xfer_pci_cfg_local_readl(struct peci_device *device,
+ u8 bus, u8 dev, u8 func, u16 reg);
+
+struct peci_request *peci_xfer_ep_pci_cfg_local_readb(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg);
+struct peci_request *peci_xfer_ep_pci_cfg_local_readw(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg);
+struct peci_request *peci_xfer_ep_pci_cfg_local_readl(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg);
+
+struct peci_request *peci_xfer_ep_pci_cfg_readb(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg);
+struct peci_request *peci_xfer_ep_pci_cfg_readw(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg);
+struct peci_request *peci_xfer_ep_pci_cfg_readl(struct peci_device *device, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg);
+
+struct peci_request *peci_xfer_ep_mmio32_readl(struct peci_device *device, u8 bar, u8 seg,
+ u8 bus, u8 dev, u8 func, u64 offset);
+
+struct peci_request *peci_xfer_ep_mmio64_readl(struct peci_device *device, u8 bar, u8 seg,
+ u8 bus, u8 dev, u8 func, u64 offset);
+/**
+ * struct peci_device_id - PECI device data to match
+ * @data: pointer to driver private data specific to device
+ * @family: device family
+ * @model: device model
+ */
+struct peci_device_id {
+ const void *data;
+ u16 family;
+ u8 model;
+};
+
+extern struct device_type peci_device_type;
+extern const struct attribute_group *peci_device_groups[];
+
+int peci_device_create(struct peci_controller *controller, u8 addr);
+void peci_device_destroy(struct peci_device *device);
+
+extern struct bus_type peci_bus_type;
+extern const struct attribute_group *peci_bus_groups[];
+
+/**
+ * struct peci_driver - PECI driver
+ * @driver: inherit device driver
+ * @probe: probe callback
+ * @remove: remove callback
+ * @id_table: PECI device match table to decide which device to bind
+ */
+struct peci_driver {
+ struct device_driver driver;
+ int (*probe)(struct peci_device *device, const struct peci_device_id *id);
+ void (*remove)(struct peci_device *device);
+ const struct peci_device_id *id_table;
+};
+
+static inline struct peci_driver *to_peci_driver(struct device_driver *d)
+{
+ return container_of(d, struct peci_driver, driver);
+}
+
+int __peci_driver_register(struct peci_driver *driver, struct module *owner,
+ const char *mod_name);
+/**
+ * peci_driver_register() - register PECI driver
+ * @driver: the driver to be registered
+ *
+ * PECI drivers that don't need to do anything special in module init should
+ * use the convenience "module_peci_driver" macro instead
+ *
+ * Return: zero on success, else a negative error code.
+ */
+#define peci_driver_register(driver) \
+ __peci_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
+void peci_driver_unregister(struct peci_driver *driver);
+
+/**
+ * module_peci_driver() - helper macro for registering a modular PECI driver
+ * @__peci_driver: peci_driver struct
+ *
+ * Helper macro for PECI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_peci_driver(__peci_driver) \
+ module_driver(__peci_driver, peci_driver_register, peci_driver_unregister)
+
+extern struct device_type peci_controller_type;
+
+int peci_controller_scan_devices(struct peci_controller *controller);
+
+#endif /* __PECI_INTERNAL_H */
diff --git a/drivers/peci/request.c b/drivers/peci/request.c
new file mode 100644
index 000000000000..8d6dd7b6b559
--- /dev/null
+++ b/drivers/peci/request.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2021 Intel Corporation
+
+#include <linux/bug.h>
+#include <linux/export.h>
+#include <linux/pci.h>
+#include <linux/peci.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/unaligned.h>
+
+#include "internal.h"
+
+#define PECI_GET_DIB_CMD 0xf7
+#define PECI_GET_DIB_WR_LEN 1
+#define PECI_GET_DIB_RD_LEN 8
+
+#define PECI_GET_TEMP_CMD 0x01
+#define PECI_GET_TEMP_WR_LEN 1
+#define PECI_GET_TEMP_RD_LEN 2
+
+#define PECI_RDPKGCFG_CMD 0xa1
+#define PECI_RDPKGCFG_WR_LEN 5
+#define PECI_RDPKGCFG_RD_LEN_BASE 1
+#define PECI_WRPKGCFG_CMD 0xa5
+#define PECI_WRPKGCFG_WR_LEN_BASE 6
+#define PECI_WRPKGCFG_RD_LEN 1
+
+#define PECI_RDIAMSR_CMD 0xb1
+#define PECI_RDIAMSR_WR_LEN 5
+#define PECI_RDIAMSR_RD_LEN 9
+#define PECI_WRIAMSR_CMD 0xb5
+#define PECI_RDIAMSREX_CMD 0xd1
+#define PECI_RDIAMSREX_WR_LEN 6
+#define PECI_RDIAMSREX_RD_LEN 9
+
+#define PECI_RDPCICFG_CMD 0x61
+#define PECI_RDPCICFG_WR_LEN 6
+#define PECI_RDPCICFG_RD_LEN 5
+#define PECI_RDPCICFG_RD_LEN_MAX 24
+#define PECI_WRPCICFG_CMD 0x65
+
+#define PECI_RDPCICFGLOCAL_CMD 0xe1
+#define PECI_RDPCICFGLOCAL_WR_LEN 5
+#define PECI_RDPCICFGLOCAL_RD_LEN_BASE 1
+#define PECI_WRPCICFGLOCAL_CMD 0xe5
+#define PECI_WRPCICFGLOCAL_WR_LEN_BASE 6
+#define PECI_WRPCICFGLOCAL_RD_LEN 1
+
+#define PECI_ENDPTCFG_TYPE_LOCAL_PCI 0x03
+#define PECI_ENDPTCFG_TYPE_PCI 0x04
+#define PECI_ENDPTCFG_TYPE_MMIO 0x05
+#define PECI_ENDPTCFG_ADDR_TYPE_PCI 0x04
+#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_D 0x05
+#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q 0x06
+#define PECI_RDENDPTCFG_CMD 0xc1
+#define PECI_RDENDPTCFG_PCI_WR_LEN 12
+#define PECI_RDENDPTCFG_MMIO_WR_LEN_BASE 10
+#define PECI_RDENDPTCFG_MMIO_D_WR_LEN 14
+#define PECI_RDENDPTCFG_MMIO_Q_WR_LEN 18
+#define PECI_RDENDPTCFG_RD_LEN_BASE 1
+#define PECI_WRENDPTCFG_CMD 0xc5
+#define PECI_WRENDPTCFG_PCI_WR_LEN_BASE 13
+#define PECI_WRENDPTCFG_MMIO_D_WR_LEN_BASE 15
+#define PECI_WRENDPTCFG_MMIO_Q_WR_LEN_BASE 19
+#define PECI_WRENDPTCFG_RD_LEN 1
+
+/* Device Specific Completion Code (CC) Definition */
+#define PECI_CC_SUCCESS 0x40
+#define PECI_CC_NEED_RETRY 0x80
+#define PECI_CC_OUT_OF_RESOURCE 0x81
+#define PECI_CC_UNAVAIL_RESOURCE 0x82
+#define PECI_CC_INVALID_REQ 0x90
+#define PECI_CC_MCA_ERROR 0x91
+#define PECI_CC_CATASTROPHIC_MCA_ERROR 0x93
+#define PECI_CC_FATAL_MCA_ERROR 0x94
+#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB 0x98
+#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR 0x9B
+#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA 0x9C
+
+#define PECI_RETRY_BIT BIT(0)
+
+#define PECI_RETRY_TIMEOUT msecs_to_jiffies(700)
+#define PECI_RETRY_INTERVAL_MIN msecs_to_jiffies(1)
+#define PECI_RETRY_INTERVAL_MAX msecs_to_jiffies(128)
+
+static u8 peci_request_data_cc(struct peci_request *req)
+{
+ return req->rx.buf[0];
+}
+
+/**
+ * peci_request_status() - return -errno based on PECI completion code
+ * @req: the PECI request that contains response data with completion code
+ *
+ * It can't be used for Ping(), GetDIB() and GetTemp() - for those commands we
+ * don't expect completion code in the response.
+ *
+ * Return: -errno
+ */
+int peci_request_status(struct peci_request *req)
+{
+ u8 cc = peci_request_data_cc(req);
+
+ if (cc != PECI_CC_SUCCESS)
+ dev_dbg(&req->device->dev, "ret: %#02x\n", cc);
+
+ switch (cc) {
+ case PECI_CC_SUCCESS:
+ return 0;
+ case PECI_CC_NEED_RETRY:
+ case PECI_CC_OUT_OF_RESOURCE:
+ case PECI_CC_UNAVAIL_RESOURCE:
+ return -EAGAIN;
+ case PECI_CC_INVALID_REQ:
+ return -EINVAL;
+ case PECI_CC_MCA_ERROR:
+ case PECI_CC_CATASTROPHIC_MCA_ERROR:
+ case PECI_CC_FATAL_MCA_ERROR:
+ case PECI_CC_PARITY_ERR_GPSB_OR_PMSB:
+ case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR:
+ case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA:
+ return -EIO;
+ }
+
+ WARN_ONCE(1, "Unknown PECI completion code: %#02x\n", cc);
+
+ return -EIO;
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_status, PECI);
+
+static int peci_request_xfer(struct peci_request *req)
+{
+ struct peci_device *device = req->device;
+ struct peci_controller *controller = to_peci_controller(device->dev.parent);
+ int ret;
+
+ mutex_lock(&controller->bus_lock);
+ ret = controller->ops->xfer(controller, device->addr, req);
+ mutex_unlock(&controller->bus_lock);
+
+ return ret;
+}
+
+static int peci_request_xfer_retry(struct peci_request *req)
+{
+ long wait_interval = PECI_RETRY_INTERVAL_MIN;
+ struct peci_device *device = req->device;
+ struct peci_controller *controller = to_peci_controller(device->dev.parent);
+ unsigned long start = jiffies;
+ int ret;
+
+ /* Don't try to use it for ping */
+ if (WARN_ON(req->tx.len == 0))
+ return 0;
+
+ do {
+ ret = peci_request_xfer(req);
+ if (ret) {
+ dev_dbg(&controller->dev, "xfer error: %d\n", ret);
+ return ret;
+ }
+
+ if (peci_request_status(req) != -EAGAIN)
+ return 0;
+
+ /* Set the retry bit to indicate a retry attempt */
+ req->tx.buf[1] |= PECI_RETRY_BIT;
+
+ if (schedule_timeout_interruptible(wait_interval))
+ return -ERESTARTSYS;
+
+ wait_interval = min_t(long, wait_interval * 2, PECI_RETRY_INTERVAL_MAX);
+ } while (time_before(jiffies, start + PECI_RETRY_TIMEOUT));
+
+ dev_dbg(&controller->dev, "request timed out\n");
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * peci_request_alloc() - allocate &struct peci_requests
+ * @device: PECI device to which request is going to be sent
+ * @tx_len: TX length
+ * @rx_len: RX length
+ *
+ * Return: A pointer to a newly allocated &struct peci_request on success or NULL otherwise.
+ */
+struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len)
+{
+ struct peci_request *req;
+
+ /*
+ * TX and RX buffers are fixed length members of peci_request, this is
+ * just a warn for developers to make sure to expand the buffers (or
+ * change the allocation method) if we go over the current limit.
+ */
+ if (WARN_ON_ONCE(tx_len > PECI_REQUEST_MAX_BUF_SIZE || rx_len > PECI_REQUEST_MAX_BUF_SIZE))
+ return NULL;
+ /*
+ * PECI controllers that we are using now don't support DMA, this
+ * should be converted to DMA API once support for controllers that do
+ * allow it is added to avoid an extra copy.
+ */
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ req->device = device;
+ req->tx.len = tx_len;
+ req->rx.len = rx_len;
+
+ return req;
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_alloc, PECI);
+
+/**
+ * peci_request_free() - free peci_request
+ * @req: the PECI request to be freed
+ */
+void peci_request_free(struct peci_request *req)
+{
+ kfree(req);
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_free, PECI);
+
+struct peci_request *peci_xfer_get_dib(struct peci_device *device)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_request_alloc(device, PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ req->tx.buf[0] = PECI_GET_DIB_CMD;
+
+ ret = peci_request_xfer(req);
+ if (ret) {
+ peci_request_free(req);
+ return ERR_PTR(ret);
+ }
+
+ return req;
+}
+EXPORT_SYMBOL_NS_GPL(peci_xfer_get_dib, PECI);
+
+struct peci_request *peci_xfer_get_temp(struct peci_device *device)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_request_alloc(device, PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ req->tx.buf[0] = PECI_GET_TEMP_CMD;
+
+ ret = peci_request_xfer(req);
+ if (ret) {
+ peci_request_free(req);
+ return ERR_PTR(ret);
+ }
+
+ return req;
+}
+EXPORT_SYMBOL_NS_GPL(peci_xfer_get_temp, PECI);
+
+static struct peci_request *
+__pkg_cfg_read(struct peci_device *device, u8 index, u16 param, u8 len)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_request_alloc(device, PECI_RDPKGCFG_WR_LEN, PECI_RDPKGCFG_RD_LEN_BASE + len);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ req->tx.buf[0] = PECI_RDPKGCFG_CMD;
+ req->tx.buf[1] = 0;
+ req->tx.buf[2] = index;
+ put_unaligned_le16(param, &req->tx.buf[3]);
+
+ ret = peci_request_xfer_retry(req);
+ if (ret) {
+ peci_request_free(req);
+ return ERR_PTR(ret);
+ }
+
+ return req;
+}
+
+static u32 __get_pci_addr(u8 bus, u8 dev, u8 func, u16 reg)
+{
+ return reg | PCI_DEVID(bus, PCI_DEVFN(dev, func)) << 12;
+}
+
+static struct peci_request *
+__pci_cfg_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func, u16 reg, u8 len)
+{
+ struct peci_request *req;
+ u32 pci_addr;
+ int ret;
+
+ req = peci_request_alloc(device, PECI_RDPCICFGLOCAL_WR_LEN,
+ PECI_RDPCICFGLOCAL_RD_LEN_BASE + len);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ pci_addr = __get_pci_addr(bus, dev, func, reg);
+
+ req->tx.buf[0] = PECI_RDPCICFGLOCAL_CMD;
+ req->tx.buf[1] = 0;
+ put_unaligned_le24(pci_addr, &req->tx.buf[2]);
+
+ ret = peci_request_xfer_retry(req);
+ if (ret) {
+ peci_request_free(req);
+ return ERR_PTR(ret);
+ }
+
+ return req;
+}
+
+static struct peci_request *
+__ep_pci_cfg_read(struct peci_device *device, u8 msg_type, u8 seg,
+ u8 bus, u8 dev, u8 func, u16 reg, u8 len)
+{
+ struct peci_request *req;
+ u32 pci_addr;
+ int ret;
+
+ req = peci_request_alloc(device, PECI_RDENDPTCFG_PCI_WR_LEN,
+ PECI_RDENDPTCFG_RD_LEN_BASE + len);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ pci_addr = __get_pci_addr(bus, dev, func, reg);
+
+ req->tx.buf[0] = PECI_RDENDPTCFG_CMD;
+ req->tx.buf[1] = 0;
+ req->tx.buf[2] = msg_type;
+ req->tx.buf[3] = 0;
+ req->tx.buf[4] = 0;
+ req->tx.buf[5] = 0;
+ req->tx.buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI;
+ req->tx.buf[7] = seg; /* PCI Segment */
+ put_unaligned_le32(pci_addr, &req->tx.buf[8]);
+
+ ret = peci_request_xfer_retry(req);
+ if (ret) {
+ peci_request_free(req);
+ return ERR_PTR(ret);
+ }
+
+ return req;
+}
+
+static struct peci_request *
+__ep_mmio_read(struct peci_device *device, u8 bar, u8 addr_type, u8 seg,
+ u8 bus, u8 dev, u8 func, u64 offset, u8 tx_len, u8 len)
+{
+ struct peci_request *req;
+ int ret;
+
+ req = peci_request_alloc(device, tx_len, PECI_RDENDPTCFG_RD_LEN_BASE + len);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ req->tx.buf[0] = PECI_RDENDPTCFG_CMD;
+ req->tx.buf[1] = 0;
+ req->tx.buf[2] = PECI_ENDPTCFG_TYPE_MMIO;
+ req->tx.buf[3] = 0; /* Endpoint ID */
+ req->tx.buf[4] = 0; /* Reserved */
+ req->tx.buf[5] = bar;
+ req->tx.buf[6] = addr_type;
+ req->tx.buf[7] = seg; /* PCI Segment */
+ req->tx.buf[8] = PCI_DEVFN(dev, func);
+ req->tx.buf[9] = bus; /* PCI Bus */
+
+ if (addr_type == PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
+ put_unaligned_le32(offset, &req->tx.buf[10]);
+ else
+ put_unaligned_le64(offset, &req->tx.buf[10]);
+
+ ret = peci_request_xfer_retry(req);
+ if (ret) {
+ peci_request_free(req);
+ return ERR_PTR(ret);
+ }
+
+ return req;
+}
+
+u8 peci_request_data_readb(struct peci_request *req)
+{
+ return req->rx.buf[1];
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_data_readb, PECI);
+
+u16 peci_request_data_readw(struct peci_request *req)
+{
+ return get_unaligned_le16(&req->rx.buf[1]);
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_data_readw, PECI);
+
+u32 peci_request_data_readl(struct peci_request *req)
+{
+ return get_unaligned_le32(&req->rx.buf[1]);
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_data_readl, PECI);
+
+u64 peci_request_data_readq(struct peci_request *req)
+{
+ return get_unaligned_le64(&req->rx.buf[1]);
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_data_readq, PECI);
+
+u64 peci_request_dib_read(struct peci_request *req)
+{
+ return get_unaligned_le64(&req->rx.buf[0]);
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_dib_read, PECI);
+
+s16 peci_request_temp_read(struct peci_request *req)
+{
+ return get_unaligned_le16(&req->rx.buf[0]);
+}
+EXPORT_SYMBOL_NS_GPL(peci_request_temp_read, PECI);
+
+#define __read_pkg_config(x, type) \
+struct peci_request *peci_xfer_pkg_cfg_##x(struct peci_device *device, u8 index, u16 param) \
+{ \
+ return __pkg_cfg_read(device, index, param, sizeof(type)); \
+} \
+EXPORT_SYMBOL_NS_GPL(peci_xfer_pkg_cfg_##x, PECI)
+
+__read_pkg_config(readb, u8);
+__read_pkg_config(readw, u16);
+__read_pkg_config(readl, u32);
+__read_pkg_config(readq, u64);
+
+#define __read_pci_config_local(x, type) \
+struct peci_request * \
+peci_xfer_pci_cfg_local_##x(struct peci_device *device, u8 bus, u8 dev, u8 func, u16 reg) \
+{ \
+ return __pci_cfg_local_read(device, bus, dev, func, reg, sizeof(type)); \
+} \
+EXPORT_SYMBOL_NS_GPL(peci_xfer_pci_cfg_local_##x, PECI)
+
+__read_pci_config_local(readb, u8);
+__read_pci_config_local(readw, u16);
+__read_pci_config_local(readl, u32);
+
+#define __read_ep_pci_config(x, msg_type, type) \
+struct peci_request * \
+peci_xfer_ep_pci_cfg_##x(struct peci_device *device, u8 seg, u8 bus, u8 dev, u8 func, u16 reg) \
+{ \
+ return __ep_pci_cfg_read(device, msg_type, seg, bus, dev, func, reg, sizeof(type)); \
+} \
+EXPORT_SYMBOL_NS_GPL(peci_xfer_ep_pci_cfg_##x, PECI)
+
+__read_ep_pci_config(local_readb, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u8);
+__read_ep_pci_config(local_readw, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u16);
+__read_ep_pci_config(local_readl, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u32);
+__read_ep_pci_config(readb, PECI_ENDPTCFG_TYPE_PCI, u8);
+__read_ep_pci_config(readw, PECI_ENDPTCFG_TYPE_PCI, u16);
+__read_ep_pci_config(readl, PECI_ENDPTCFG_TYPE_PCI, u32);
+
+#define __read_ep_mmio(x, y, addr_type, type1, type2) \
+struct peci_request *peci_xfer_ep_mmio##y##_##x(struct peci_device *device, u8 bar, u8 seg, \
+ u8 bus, u8 dev, u8 func, u64 offset) \
+{ \
+ return __ep_mmio_read(device, bar, addr_type, seg, bus, dev, func, \
+ offset, PECI_RDENDPTCFG_MMIO_WR_LEN_BASE + sizeof(type1), \
+ sizeof(type2)); \
+} \
+EXPORT_SYMBOL_NS_GPL(peci_xfer_ep_mmio##y##_##x, PECI)
+
+__read_ep_mmio(readl, 32, PECI_ENDPTCFG_ADDR_TYPE_MMIO_D, u32, u32);
+__read_ep_mmio(readl, 64, PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q, u64, u32);
diff --git a/drivers/peci/sysfs.c b/drivers/peci/sysfs.c
new file mode 100644
index 000000000000..db9ef05776e3
--- /dev/null
+++ b/drivers/peci/sysfs.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2021 Intel Corporation
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/peci.h>
+
+#include "internal.h"
+
+static int rescan_controller(struct device *dev, void *data)
+{
+ if (dev->type != &peci_controller_type)
+ return 0;
+
+ return peci_controller_scan_devices(to_peci_controller(dev));
+}
+
+static ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count)
+{
+ bool res;
+ int ret;
+
+ ret = kstrtobool(buf, &res);
+ if (ret)
+ return ret;
+
+ if (!res)
+ return count;
+
+ ret = bus_for_each_dev(&peci_bus_type, NULL, NULL, rescan_controller);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static BUS_ATTR_WO(rescan);
+
+static struct attribute *peci_bus_attrs[] = {
+ &bus_attr_rescan.attr,
+ NULL
+};
+
+static const struct attribute_group peci_bus_group = {
+ .attrs = peci_bus_attrs,
+};
+
+const struct attribute_group *peci_bus_groups[] = {
+ &peci_bus_group,
+ NULL
+};
+
+static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct peci_device *device = to_peci_device(dev);
+ bool res;
+ int ret;
+
+ ret = kstrtobool(buf, &res);
+ if (ret)
+ return ret;
+
+ if (res && device_remove_file_self(dev, attr))
+ peci_device_destroy(device);
+
+ return count;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, remove_store);
+
+static struct attribute *peci_device_attrs[] = {
+ &dev_attr_remove.attr,
+ NULL
+};
+
+static const struct attribute_group peci_device_group = {
+ .attrs = peci_device_attrs,
+};
+
+const struct attribute_group *peci_device_groups[] = {
+ &peci_device_group,
+ NULL
+};
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index 788dd5cdbb7d..d5f3b42eb8ce 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -43,7 +43,7 @@
#define REG_PHYCTL_A33 0x10
#define REG_PHY_OTGCTL 0x20
-#define REG_PMU_UNK1 0x10
+#define REG_HCI_PHY_CTL 0x10
#define PHYCTL_DATA BIT(7)
@@ -82,6 +82,7 @@
/* A83T specific control bits for PHY0 */
#define PHY_CTL_VBUSVLDEXT BIT(5)
#define PHY_CTL_SIDDQ BIT(3)
+#define PHY_CTL_H3_SIDDQ BIT(1)
/* A83T specific control bits for PHY2 HSIC */
#define SUNXI_EHCI_HS_FORCE BIT(20)
@@ -115,9 +116,9 @@ struct sun4i_usb_phy_cfg {
int hsic_index;
enum sun4i_usb_phy_type type;
u32 disc_thresh;
+ u32 hci_phy_ctl_clear;
u8 phyctl_offset;
bool dedicated_clocks;
- bool enable_pmu_unk1;
bool phy0_dual_route;
int missing_phys;
};
@@ -288,6 +289,12 @@ static int sun4i_usb_phy_init(struct phy *_phy)
return ret;
}
+ if (phy->pmu && data->cfg->hci_phy_ctl_clear) {
+ val = readl(phy->pmu + REG_HCI_PHY_CTL);
+ val &= ~data->cfg->hci_phy_ctl_clear;
+ writel(val, phy->pmu + REG_HCI_PHY_CTL);
+ }
+
if (data->cfg->type == sun8i_a83t_phy ||
data->cfg->type == sun50i_h6_phy) {
if (phy->index == 0) {
@@ -297,11 +304,6 @@ static int sun4i_usb_phy_init(struct phy *_phy)
writel(val, data->base + data->cfg->phyctl_offset);
}
} else {
- if (phy->pmu && data->cfg->enable_pmu_unk1) {
- val = readl(phy->pmu + REG_PMU_UNK1);
- writel(val & ~2, phy->pmu + REG_PMU_UNK1);
- }
-
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
@@ -863,7 +865,6 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
@@ -872,7 +873,6 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
@@ -881,7 +881,6 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
@@ -890,7 +889,6 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
@@ -899,7 +897,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
@@ -908,7 +905,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
@@ -925,7 +921,7 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
.phy0_dual_route = true,
};
@@ -935,7 +931,7 @@ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
.phy0_dual_route = true,
};
@@ -945,7 +941,16 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
+ .phy0_dual_route = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = {
+ .num_phys = 2,
+ .type = sun50i_h6_phy,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+ .hci_phy_ctl_clear = PHY_CTL_SIDDQ,
.phy0_dual_route = true,
};
@@ -955,14 +960,13 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
.phy0_dual_route = true,
};
static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
.num_phys = 4,
.type = sun50i_h6_phy,
- .disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
.phy0_dual_route = true,
@@ -980,6 +984,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
{ .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
+ { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg },
{ .compatible = "allwinner,sun50i-a64-usb-phy",
.data = &sun50i_a64_cfg},
{ .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
index 2b3c0d730f20..db17c3448bfe 100644
--- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c
+++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
@@ -114,8 +114,10 @@ static int phy_meson_gxl_usb2_init(struct phy *phy)
return ret;
ret = clk_prepare_enable(priv->clk);
- if (ret)
+ if (ret) {
+ reset_control_rearm(priv->reset);
return ret;
+ }
return 0;
}
@@ -125,6 +127,7 @@ static int phy_meson_gxl_usb2_exit(struct phy *phy)
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
clk_disable_unprepare(priv->clk);
+ reset_control_rearm(priv->reset);
return 0;
}
diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c
index cf10bed40528..dd96763911b8 100644
--- a/drivers/phy/amlogic/phy-meson8b-usb2.c
+++ b/drivers/phy/amlogic/phy-meson8b-usb2.c
@@ -154,6 +154,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
ret = clk_prepare_enable(priv->clk_usb_general);
if (ret) {
dev_err(&phy->dev, "Failed to enable USB general clock\n");
+ reset_control_rearm(priv->reset);
return ret;
}
@@ -161,6 +162,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
if (ret) {
dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
clk_disable_unprepare(priv->clk_usb_general);
+ reset_control_rearm(priv->reset);
return ret;
}
@@ -199,6 +201,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
dev_warn(&phy->dev, "USB ID detect failed!\n");
clk_disable_unprepare(priv->clk_usb);
clk_disable_unprepare(priv->clk_usb_general);
+ reset_control_rearm(priv->reset);
return -EINVAL;
}
}
@@ -218,6 +221,7 @@ static int phy_meson8b_usb2_power_off(struct phy *phy)
clk_disable_unprepare(priv->clk_usb);
clk_disable_unprepare(priv->clk_usb_general);
+ reset_control_rearm(priv->reset);
/* power off the PHY by putting it into reset mode */
regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET,
@@ -265,8 +269,9 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk_usb);
priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
- if (PTR_ERR(priv->reset) == -EPROBE_DEFER)
- return PTR_ERR(priv->reset);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset),
+ "Failed to get the reset line");
priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1);
if (priv->dr_mode == USB_DR_MODE_UNKNOWN) {
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
index e63457e145c7..d2524b70ea16 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
+++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
@@ -47,6 +47,8 @@
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000
+#define USB_CTRL_USB_PM_XHC_PME_EN_MASK 0x00000010
+#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK 0x00000008
#define USB_CTRL_USB_PM_STATUS 0x08
#define USB_CTRL_USB_DEVICE_CTL1 0x10
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003
@@ -190,10 +192,6 @@ static void usb_init_common(struct brcm_usb_init_params *params)
pr_debug("%s\n", __func__);
- USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
- /* 1 millisecond - for USB clocks to settle down */
- usleep_range(1000, 2000);
-
if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
@@ -222,6 +220,17 @@ static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params,
USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en);
}
+static void usb_wake_enable_7216(struct brcm_usb_init_params *params,
+ bool enable)
+{
+ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+ if (enable)
+ USB_CTRL_SET(ctrl, USB_PM, XHC_PME_EN);
+ else
+ USB_CTRL_UNSET(ctrl, USB_PM, XHC_PME_EN);
+}
+
static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
@@ -295,6 +304,20 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
usb2_eye_fix_7211b0(params);
}
+static void usb_init_common_7216(struct brcm_usb_init_params *params)
+{
+ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+ USB_CTRL_UNSET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN);
+ USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
+
+ /* 1 millisecond - for USB clocks to settle down */
+ usleep_range(1000, 2000);
+
+ usb_wake_enable_7216(params, false);
+ usb_init_common(params);
+}
+
static void usb_init_xhci(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
@@ -302,14 +325,20 @@ static void usb_init_xhci(struct brcm_usb_init_params *params)
xhci_soft_reset(params, 0);
}
-static void usb_uninit_common(struct brcm_usb_init_params *params)
+static void usb_uninit_common_7216(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
pr_debug("%s\n", __func__);
- USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
+ if (!params->wake_enabled) {
+ USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
+ /* Switch to using slower clock during suspend to save power */
+ USB_CTRL_SET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN);
+ } else {
+ usb_wake_enable_7216(params, true);
+ }
}
static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params)
@@ -371,9 +400,9 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
static const struct brcm_usb_init_ops bcm7216_ops = {
.init_ipp = usb_init_ipp,
- .init_common = usb_init_common,
+ .init_common = usb_init_common_7216,
.init_xhci = usb_init_xhci,
- .uninit_common = usb_uninit_common,
+ .uninit_common = usb_uninit_common_7216,
.uninit_xhci = usb_uninit_xhci,
.get_dual_select = usb_get_dual_select,
.set_dual_select = usb_set_dual_select,
@@ -396,6 +425,7 @@ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
params->family_name = "7216";
params->ops = &bcm7216_ops;
+ params->suspend_with_clocks = true;
}
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c
index 9391ab42a12b..dd0f66288fbd 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init.c
+++ b/drivers/phy/broadcom/phy-brcm-usb-init.c
@@ -79,6 +79,7 @@
enum brcm_family_type {
BRCM_FAMILY_3390A0,
+ BRCM_FAMILY_4908,
BRCM_FAMILY_7250B0,
BRCM_FAMILY_7271A0,
BRCM_FAMILY_7364A0,
@@ -96,6 +97,7 @@ enum brcm_family_type {
static const char *family_names[BRCM_FAMILY_COUNT] = {
USB_BRCM_FAMILY(3390A0),
+ USB_BRCM_FAMILY(4908),
USB_BRCM_FAMILY(7250B0),
USB_BRCM_FAMILY(7271A0),
USB_BRCM_FAMILY(7364A0),
@@ -203,6 +205,27 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
+ /* 4908 */
+ [BRCM_FAMILY_4908] = {
+ 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */
+ 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
+ 0, /* USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */
+ 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */
+ 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
+ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
+ USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
+ USB_CTRL_USB_PM_USB_PWRDN_MASK,
+ 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
+ 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
+ 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
+ 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
+ 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
+ 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
+ 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
+ 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK */
+ 0, /* USB_CTRL_SETUP ENDIAN bits */
+ },
/* 7250b0 */
[BRCM_FAMILY_7250B0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
@@ -559,6 +582,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params)
*/
switch (params->selected_family) {
case BRCM_FAMILY_3390A0:
+ case BRCM_FAMILY_4908:
case BRCM_FAMILY_7250B0:
case BRCM_FAMILY_7366C0:
case BRCM_FAMILY_74371A0:
@@ -1004,6 +1028,18 @@ static const struct brcm_usb_init_ops bcm7445_ops = {
.set_dual_select = usb_set_dual_select,
};
+void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params)
+{
+ int fam;
+
+ fam = BRCM_FAMILY_4908;
+ params->selected_family = fam;
+ params->usb_reg_bits_map =
+ &usb_reg_bits_map_table[fam][0];
+ params->family_name = family_names[fam];
+ params->ops = &bcm7445_ops;
+}
+
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params)
{
int fam;
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h
index a39f30fa2e99..1ccb5ddab865 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init.h
+++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
@@ -64,6 +64,7 @@ struct brcm_usb_init_params {
bool suspend_with_clocks;
};
+void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c
index 0f1deb6e0eab..2cb3779fcdf8 100644
--- a/drivers/phy/broadcom/phy-brcm-usb.c
+++ b/drivers/phy/broadcom/phy-brcm-usb.c
@@ -283,6 +283,15 @@ static const struct attribute_group brcm_usb_phy_group = {
.attrs = brcm_usb_phy_attrs,
};
+static const struct match_chip_info chip_info_4908 = {
+ .init_func = &brcm_usb_dvr_init_4908,
+ .required_regs = {
+ BRCM_REGS_CTRL,
+ BRCM_REGS_XHCI_EC,
+ -1,
+ },
+};
+
static const struct match_chip_info chip_info_7216 = {
.init_func = &brcm_usb_dvr_init_7216,
.required_regs = {
@@ -318,7 +327,7 @@ static const struct match_chip_info chip_info_7445 = {
static const struct of_device_id brcm_usb_dt_ids[] = {
{
.compatible = "brcm,bcm4908-usb-phy",
- .data = &chip_info_7445,
+ .data = &chip_info_4908,
},
{
.compatible = "brcm,bcm7216-usb-phy",
diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
index a62910ff5591..1adde2d99ae7 100644
--- a/drivers/phy/cadence/Kconfig
+++ b/drivers/phy/cadence/Kconfig
@@ -22,6 +22,14 @@ config PHY_CADENCE_DPHY
system. If M is selected, the module will be called
cdns-dphy.
+config PHY_CADENCE_DPHY_RX
+ tristate "Cadence D-PHY Rx Support"
+ depends on HAS_IOMEM && OF
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Support for Cadence D-PHY in Rx configuration.
+
config PHY_CADENCE_SIERRA
tristate "Cadence Sierra PHY Driver"
depends on OF && HAS_IOMEM && RESET_CONTROLLER
diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
index 26e16bd34efe..e17f035ddece 100644
--- a/drivers/phy/cadence/Makefile
+++ b/drivers/phy/cadence/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_CADENCE_TORRENT) += phy-cadence-torrent.o
obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o
+obj-$(CONFIG_PHY_CADENCE_DPHY_RX) += cdns-dphy-rx.o
obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o
obj-$(CONFIG_PHY_CADENCE_SALVO) += phy-cadence-salvo.o
diff --git a/drivers/phy/cadence/cdns-dphy-rx.c b/drivers/phy/cadence/cdns-dphy-rx.c
new file mode 100644
index 000000000000..572c70089a94
--- /dev/null
+++ b/drivers/phy/cadence/cdns-dphy-rx.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+
+#define DPHY_PMA_CMN(reg) (reg)
+#define DPHY_PCS(reg) (0xb00 + (reg))
+#define DPHY_ISO(reg) (0xc00 + (reg))
+
+#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
+#define DPHY_CMN_RX_MODE_EN BIT(10)
+#define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1)
+#define DPHY_CMN_SSM_EN BIT(0)
+
+#define DPHY_CMN_RX_BANDGAP_TIMER 0x14
+
+#define DPHY_BAND_CFG DPHY_PCS(0x0)
+#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5)
+#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0)
+
+#define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8)
+#define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa
+
+#define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc)
+#define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa
+
+#define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10)
+#define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14)
+#define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20)
+#define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30)
+#define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c)
+
+#define DPHY_ISO_LANE_READY_BIT 0
+#define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL
+
+#define DPHY_LANES_MIN 1
+#define DPHY_LANES_MAX 4
+
+struct cdns_dphy_rx {
+ void __iomem *regs;
+ struct device *dev;
+ struct phy *phy;
+};
+
+struct cdns_dphy_rx_band {
+ /* Rates are in Mbps. */
+ unsigned int min_rate;
+ unsigned int max_rate;
+};
+
+/* Order of bands is important since the index is the band number. */
+static const struct cdns_dphy_rx_band bands[] = {
+ { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 },
+ { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 },
+ { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 },
+ { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 },
+ { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 }
+};
+
+static int cdns_dphy_rx_power_on(struct phy *phy)
+{
+ struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
+
+ /* Start RX state machine. */
+ writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN |
+ FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK,
+ DPHY_CMN_RX_BANDGAP_TIMER),
+ dphy->regs + DPHY_CMN_SSM);
+
+ return 0;
+}
+
+static int cdns_dphy_rx_power_off(struct phy *phy)
+{
+ struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
+
+ writel(0, dphy->regs + DPHY_CMN_SSM);
+
+ return 0;
+}
+
+static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate)
+{
+ unsigned int rate, i;
+
+ rate = hs_clk_rate / 1000000UL;
+ /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */
+ rate *= 2;
+
+ if (rate < bands[0].min_rate)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < ARRAY_SIZE(bands); i++)
+ if (rate < bands[i].max_rate)
+ return i;
+
+ return -EOPNOTSUPP;
+}
+
+static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr,
+ unsigned int bit)
+{
+ u32 val;
+
+ return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10,
+ DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000);
+}
+
+static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy,
+ unsigned int lanes)
+{
+ static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0,
+ DPHY_ISO_DL_CTRL_L1,
+ DPHY_ISO_DL_CTRL_L2,
+ DPHY_ISO_DL_CTRL_L3};
+ void __iomem *reg = dphy->regs;
+ unsigned int i;
+ int ret;
+
+ /* Clock lane */
+ ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L,
+ DPHY_ISO_LANE_READY_BIT);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < lanes; i++) {
+ ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i],
+ DPHY_ISO_LANE_READY_BIT);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cdns_dphy_rx_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
+ unsigned int reg, lanes = opts->mipi_dphy.lanes;
+ int band_ctrl, ret;
+
+ /* Data lanes. Minimum one lane is mandatory. */
+ if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX)
+ return -EINVAL;
+
+ band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
+ if (band_ctrl < 0)
+ return band_ctrl;
+
+ reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
+ FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
+ writel(reg, dphy->regs + DPHY_BAND_CFG);
+
+ /*
+ * Set the required power island phase 2 time. This is mandated by DPHY
+ * specs.
+ */
+ reg = DPHY_POWER_ISLAND_EN_DATA_VAL;
+ writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA);
+ reg = DPHY_POWER_ISLAND_EN_CLK_VAL;
+ writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK);
+
+ ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes);
+ if (ret) {
+ dev_err(dphy->dev, "DPHY wait for lane ready timeout\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode,
+ int submode, union phy_configure_opts *opts)
+{
+ int ret;
+
+ if (mode != PHY_MODE_MIPI_DPHY)
+ return -EINVAL;
+
+ ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
+ if (ret < 0)
+ return ret;
+
+ return phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+}
+
+static const struct phy_ops cdns_dphy_rx_ops = {
+ .power_on = cdns_dphy_rx_power_on,
+ .power_off = cdns_dphy_rx_power_off,
+ .configure = cdns_dphy_rx_configure,
+ .validate = cdns_dphy_rx_validate,
+};
+
+static int cdns_dphy_rx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct cdns_dphy_rx *dphy;
+
+ dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL);
+ if (!dphy)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, dphy);
+ dphy->dev = dev;
+
+ dphy->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dphy->regs))
+ return PTR_ERR(dphy->regs);
+
+ dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops);
+ if (IS_ERR(dphy->phy)) {
+ dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy));
+ return PTR_ERR(dphy->phy);
+ }
+
+ phy_set_drvdata(dphy->phy, dphy);
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider)) {
+ dev_err(dev, "Failed to register PHY provider: %ld\n",
+ PTR_ERR(provider));
+ return PTR_ERR(provider);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id cdns_dphy_rx_of_match[] = {
+ { .compatible = "cdns,dphy-rx" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match);
+
+static struct platform_driver cdns_dphy_rx_platform_driver = {
+ .probe = cdns_dphy_rx_probe,
+ .driver = {
+ .name = "cdns-mipi-dphy-rx",
+ .of_match_table = cdns_dphy_rx_of_match,
+ },
+};
+module_platform_driver(cdns_dphy_rx_platform_driver);
+
+MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>");
+MODULE_DESCRIPTION("Cadence D-PHY Rx Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c
index 51c0b98f5fd7..e569f5f67578 100644
--- a/drivers/phy/cadence/phy-cadence-salvo.c
+++ b/drivers/phy/cadence/phy-cadence-salvo.c
@@ -263,14 +263,9 @@ static int cdns_salvo_phy_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct cdns_salvo_phy *salvo_phy;
- const struct of_device_id *match;
struct cdns_salvo_data *data;
- match = of_match_device(cdns_salvo_phy_of_match, dev);
- if (!match)
- return -EINVAL;
-
- data = (struct cdns_salvo_data *)match->data;
+ data = (struct cdns_salvo_data *)of_device_get_match_data(dev);
salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL);
if (!salvo_phy)
return -ENOMEM;
diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index e265647e29a2..6b917f7bddbe 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -370,6 +370,7 @@ struct cdns_sierra_phy {
int nsubnodes;
u32 num_lanes;
bool autoconf;
+ int already_configured;
struct clk_onecell_data clk_data;
struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS];
};
@@ -517,7 +518,7 @@ static int cdns_sierra_phy_init(struct phy *gphy)
int i, j;
/* Initialise the PHY registers, unless auto configured */
- if (phy->autoconf || phy->nsubnodes > 1)
+ if (phy->autoconf || phy->already_configured || phy->nsubnodes > 1)
return 0;
clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
@@ -646,6 +647,18 @@ static const struct phy_ops ops = {
.owner = THIS_MODULE,
};
+static int cdns_sierra_noop_phy_on(struct phy *gphy)
+{
+ usleep_range(5000, 10000);
+
+ return 0;
+}
+
+static const struct phy_ops noop_ops = {
+ .power_on = cdns_sierra_noop_phy_on,
+ .owner = THIS_MODULE,
+};
+
static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
{
struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
@@ -1118,13 +1131,6 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
struct clk *clk;
int ret;
- clk = devm_clk_get_optional(dev, "phy_clk");
- if (IS_ERR(clk)) {
- dev_err(dev, "failed to get clock phy_clk\n");
- return PTR_ERR(clk);
- }
- sp->input_clks[PHY_CLK] = clk;
-
clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk_dig_div clock not found\n");
@@ -1160,17 +1166,33 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
return 0;
}
-static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
+static int cdns_sierra_phy_clk(struct cdns_sierra_phy *sp)
{
+ struct device *dev = sp->dev;
+ struct clk *clk;
int ret;
+ clk = devm_clk_get_optional(dev, "phy_clk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get clock phy_clk\n");
+ return PTR_ERR(clk);
+ }
+ sp->input_clks[PHY_CLK] = clk;
+
ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
if (ret)
return ret;
+ return 0;
+}
+
+static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
+{
+ int ret;
+
ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
if (ret)
- goto err_pll_cmnlc;
+ return ret;
ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
if (ret)
@@ -1181,9 +1203,6 @@ static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
err_pll_cmnlc1:
clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
-err_pll_cmnlc:
- clk_disable_unprepare(sp->input_clks[PHY_CLK]);
-
return ret;
}
@@ -1191,7 +1210,8 @@ static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp)
{
clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
- clk_disable_unprepare(sp->input_clks[PHY_CLK]);
+ if (!sp->already_configured)
+ clk_disable_unprepare(sp->input_clks[PHY_CLK]);
}
static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
@@ -1382,22 +1402,30 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = cdns_sierra_phy_get_resets(sp, dev);
- if (ret)
- goto unregister_clk;
-
ret = cdns_sierra_phy_enable_clocks(sp);
if (ret)
goto unregister_clk;
- /* Enable APB */
- reset_control_deassert(sp->apb_rst);
+ regmap_field_read(sp->pma_cmn_ready, &sp->already_configured);
+
+ if (!sp->already_configured) {
+ ret = cdns_sierra_phy_clk(sp);
+ if (ret)
+ goto clk_disable;
+
+ ret = cdns_sierra_phy_get_resets(sp, dev);
+ if (ret)
+ goto clk_disable;
+
+ /* Enable APB */
+ reset_control_deassert(sp->apb_rst);
+ }
/* Check that PHY is present */
regmap_field_read(sp->macro_id_type, &id_value);
if (sp->init_data->id_value != id_value) {
ret = -EINVAL;
- goto clk_disable;
+ goto ctrl_assert;
}
sp->autoconf = of_property_read_bool(dn, "cdns,autoconf");
@@ -1433,8 +1461,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
sp->num_lanes += sp->phys[node].num_lanes;
- gphy = devm_phy_create(dev, child, &ops);
-
+ if (!sp->already_configured)
+ gphy = devm_phy_create(dev, child, &ops);
+ else
+ gphy = devm_phy_create(dev, child, &noop_ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
of_node_put(child);
@@ -1455,7 +1485,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
}
/* If more than one subnode, configure the PHY as multilink */
- if (!sp->autoconf && sp->nsubnodes > 1) {
+ if (!sp->already_configured && !sp->autoconf && sp->nsubnodes > 1) {
ret = cdns_sierra_phy_configure_multilink(sp);
if (ret)
goto put_control;
@@ -1473,9 +1503,11 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
put_control:
while (--node >= 0)
reset_control_put(sp->phys[node].lnk_rst);
+ctrl_assert:
+ if (!sp->already_configured)
+ reset_control_assert(sp->apb_rst);
clk_disable:
cdns_sierra_phy_disable_clocks(sp);
- reset_control_assert(sp->apb_rst);
unregister_clk:
cdns_sierra_clk_unregister(sp);
return ret;
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 0e91cd99c36b..8d945211c7b4 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -1,4 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+if (ARCH_MXC && ARM64) || COMPILE_TEST
+
config PHY_FSL_IMX8MQ_USB
tristate "Freescale i.MX8M USB3 PHY"
depends on OF && HAS_IOMEM
@@ -32,3 +35,5 @@ config PHY_FSL_LYNX_28G
found on NXP's Layerscape platforms such as LX2160A.
Used to change the protocol running on SerDes lanes at runtime.
Only useful for a restricted set of Ethernet protocols.
+
+endif
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
index 04b1aafb29f4..f1eb03ba25d6 100644
--- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -5,9 +5,9 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
-#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
#include <linux/module.h>
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+
#include <dt-bindings/phy/phy-imx8-pcie.h>
#define IMX8MM_PCIE_PHY_CMN_REG061 0x184
diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
index 6781488cfc58..a4d7d9bd100d 100644
--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
@@ -5,12 +5,16 @@
* Authors:
* Evan Wang <xswang@marvell.com>
* Miquèl Raynal <miquel.raynal@bootlin.com>
+ * Pali Rohár <pali@kernel.org>
+ * Marek Behún <kabel@kernel.org>
*
* Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
- * SMC call initial support done by Grzegorz Jaszczyk.
+ * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org>
+ * and Marek Behún <kabel@kernel.org>.
*/
-#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
@@ -18,109 +22,1118 @@
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
-#define MVEBU_A3700_COMPHY_LANES 3
-#define MVEBU_A3700_COMPHY_PORTS 2
-
-/* COMPHY Fast SMC function identifiers */
-#define COMPHY_SIP_POWER_ON 0x82000001
-#define COMPHY_SIP_POWER_OFF 0x82000002
-#define COMPHY_SIP_PLL_LOCK 0x82000003
-
-#define COMPHY_FW_MODE_SATA 0x1
-#define COMPHY_FW_MODE_SGMII 0x2
-#define COMPHY_FW_MODE_2500BASEX 0x3
-#define COMPHY_FW_MODE_USB3H 0x4
-#define COMPHY_FW_MODE_USB3D 0x5
-#define COMPHY_FW_MODE_PCIE 0x6
-#define COMPHY_FW_MODE_USB3 0xa
-
-#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
-#define COMPHY_FW_SPEED_2_5G 1
-#define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */
-#define COMPHY_FW_SPEED_5G 3
-#define COMPHY_FW_SPEED_MAX 0x3F
-
-#define COMPHY_FW_MODE(mode) ((mode) << 12)
-#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
- ((idx) << 8) | \
- ((speed) << 2))
-#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
- ((width) << 18))
+#define PLL_SET_DELAY_US 600
+#define COMPHY_PLL_SLEEP 1000
+#define COMPHY_PLL_TIMEOUT 150000
+
+/* Comphy lane2 indirect access register offset */
+#define COMPHY_LANE2_INDIR_ADDR 0x0
+#define COMPHY_LANE2_INDIR_DATA 0x4
+
+/* SATA and USB3 PHY offset compared to SATA PHY */
+#define COMPHY_LANE2_REGS_BASE 0x200
+
+/*
+ * When accessing common PHY lane registers directly, we need to shift by 1,
+ * since the registers are 16-bit.
+ */
+#define COMPHY_LANE_REG_DIRECT(reg) (((reg) & 0x7FF) << 1)
+
+/* COMPHY registers */
+#define COMPHY_POWER_PLL_CTRL 0x01
+#define PU_IVREF_BIT BIT(15)
+#define PU_PLL_BIT BIT(14)
+#define PU_RX_BIT BIT(13)
+#define PU_TX_BIT BIT(12)
+#define PU_TX_INTP_BIT BIT(11)
+#define PU_DFE_BIT BIT(10)
+#define RESET_DTL_RX_BIT BIT(9)
+#define PLL_LOCK_BIT BIT(8)
+#define REF_FREF_SEL_MASK GENMASK(4, 0)
+#define REF_FREF_SEL_SERDES_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x1)
+#define REF_FREF_SEL_SERDES_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
+#define REF_FREF_SEL_SERDES_50MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x4)
+#define REF_FREF_SEL_PCIE_USB3_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x2)
+#define REF_FREF_SEL_PCIE_USB3_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
+#define COMPHY_MODE_MASK GENMASK(7, 5)
+#define COMPHY_MODE_SATA FIELD_PREP(COMPHY_MODE_MASK, 0x0)
+#define COMPHY_MODE_PCIE FIELD_PREP(COMPHY_MODE_MASK, 0x3)
+#define COMPHY_MODE_SERDES FIELD_PREP(COMPHY_MODE_MASK, 0x4)
+#define COMPHY_MODE_USB3 FIELD_PREP(COMPHY_MODE_MASK, 0x5)
+
+#define COMPHY_KVCO_CAL_CTRL 0x02
+#define USE_MAX_PLL_RATE_BIT BIT(12)
+#define SPEED_PLL_MASK GENMASK(7, 2)
+#define SPEED_PLL_VALUE_16 FIELD_PREP(SPEED_PLL_MASK, 0x10)
+
+#define COMPHY_DIG_LOOPBACK_EN 0x23
+#define SEL_DATA_WIDTH_MASK GENMASK(11, 10)
+#define DATA_WIDTH_10BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0)
+#define DATA_WIDTH_20BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1)
+#define DATA_WIDTH_40BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2)
+#define PLL_READY_TX_BIT BIT(4)
+
+#define COMPHY_SYNC_PATTERN 0x24
+#define TXD_INVERT_BIT BIT(10)
+#define RXD_INVERT_BIT BIT(11)
+
+#define COMPHY_SYNC_MASK_GEN 0x25
+#define PHY_GEN_MAX_MASK GENMASK(11, 10)
+#define PHY_GEN_MAX_USB3_5G FIELD_PREP(PHY_GEN_MAX_MASK, 0x1)
+
+#define COMPHY_ISOLATION_CTRL 0x26
+#define PHY_ISOLATE_MODE BIT(15)
+
+#define COMPHY_GEN2_SET2 0x3e
+#define GS2_TX_SSC_AMP_MASK GENMASK(15, 9)
+#define GS2_TX_SSC_AMP_4128 FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20)
+#define GS2_VREG_RXTX_MAS_ISET_MASK GENMASK(8, 7)
+#define GS2_VREG_RXTX_MAS_ISET_60U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x0)
+#define GS2_VREG_RXTX_MAS_ISET_80U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x1)
+#define GS2_VREG_RXTX_MAS_ISET_100U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x2)
+#define GS2_VREG_RXTX_MAS_ISET_120U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x3)
+#define GS2_RSVD_6_0_MASK GENMASK(6, 0)
+
+#define COMPHY_GEN3_SET2 0x3f
+
+#define COMPHY_IDLE_SYNC_EN 0x48
+#define IDLE_SYNC_EN BIT(12)
+
+#define COMPHY_MISC_CTRL0 0x4F
+#define CLK100M_125M_EN BIT(4)
+#define TXDCLK_2X_SEL BIT(6)
+#define CLK500M_EN BIT(7)
+#define PHY_REF_CLK_SEL BIT(10)
+
+#define COMPHY_SFT_RESET 0x52
+#define SFT_RST BIT(9)
+#define SFT_RST_NO_REG BIT(10)
+
+#define COMPHY_MISC_CTRL1 0x73
+#define SEL_BITS_PCIE_FORCE BIT(15)
+
+#define COMPHY_GEN2_SET3 0x112
+#define GS3_FFE_CAP_SEL_MASK GENMASK(3, 0)
+#define GS3_FFE_CAP_SEL_VALUE FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF)
+
+/* PIPE registers */
+#define COMPHY_PIPE_LANE_CFG0 0x180
+#define PRD_TXDEEMPH0_MASK BIT(0)
+#define PRD_TXMARGIN_MASK GENMASK(3, 1)
+#define PRD_TXSWING_MASK BIT(4)
+#define CFG_TX_ALIGN_POS_MASK GENMASK(8, 5)
+
+#define COMPHY_PIPE_LANE_CFG1 0x181
+#define PRD_TXDEEMPH1_MASK BIT(15)
+#define USE_MAX_PLL_RATE_EN BIT(9)
+#define TX_DET_RX_MODE BIT(6)
+#define GEN2_TX_DATA_DLY_MASK GENMASK(4, 3)
+#define GEN2_TX_DATA_DLY_DEFT FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2)
+#define TX_ELEC_IDLE_MODE_EN BIT(0)
+
+#define COMPHY_PIPE_LANE_STAT1 0x183
+#define TXDCLK_PCLK_EN BIT(0)
+
+#define COMPHY_PIPE_LANE_CFG4 0x188
+#define SPREAD_SPECTRUM_CLK_EN BIT(7)
+
+#define COMPHY_PIPE_RST_CLK_CTRL 0x1C1
+#define PIPE_SOFT_RESET BIT(0)
+#define PIPE_REG_RESET BIT(1)
+#define MODE_CORE_CLK_FREQ_SEL BIT(9)
+#define MODE_PIPE_WIDTH_32 BIT(3)
+#define MODE_REFDIV_MASK GENMASK(5, 4)
+#define MODE_REFDIV_BY_4 FIELD_PREP(MODE_REFDIV_MASK, 0x2)
+
+#define COMPHY_PIPE_TEST_MODE_CTRL 0x1C2
+#define MODE_MARGIN_OVERRIDE BIT(2)
+
+#define COMPHY_PIPE_CLK_SRC_LO 0x1C3
+#define MODE_CLK_SRC BIT(0)
+#define BUNDLE_PERIOD_SEL BIT(1)
+#define BUNDLE_PERIOD_SCALE_MASK GENMASK(3, 2)
+#define BUNDLE_SAMPLE_CTRL BIT(4)
+#define PLL_READY_DLY_MASK GENMASK(7, 5)
+#define CFG_SEL_20B BIT(15)
+
+#define COMPHY_PIPE_PWR_MGM_TIM1 0x1D0
+#define CFG_PM_OSCCLK_WAIT_MASK GENMASK(15, 12)
+#define CFG_PM_RXDEN_WAIT_MASK GENMASK(11, 8)
+#define CFG_PM_RXDEN_WAIT_1_UNIT FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1)
+#define CFG_PM_RXDLOZ_WAIT_MASK GENMASK(7, 0)
+#define CFG_PM_RXDLOZ_WAIT_7_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7)
+#define CFG_PM_RXDLOZ_WAIT_12_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC)
+
+/*
+ * This register is not from PHY lane register space. It only exists in the
+ * indirect register space, before the actual PHY lane 2 registers. So the
+ * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE.
+ * It is used only for SATA PHY initialization.
+ */
+#define COMPHY_RESERVED_REG 0x0E
+#define PHYCTRL_FRM_PIN_BIT BIT(13)
+
+/* South Bridge PHY Configuration Registers */
+#define COMPHY_PHY_REG(lane, reg) (((1 - (lane)) * 0x28) + ((reg) & 0x3f))
+
+/*
+ * lane0: USB3/GbE1 PHY Configuration 1
+ * lane1: PCIe/GbE0 PHY Configuration 1
+ * (used only by SGMII code)
+ */
+#define COMPHY_PHY_CFG1 0x0
+#define PIN_PU_IVREF_BIT BIT(1)
+#define PIN_RESET_CORE_BIT BIT(11)
+#define PIN_RESET_COMPHY_BIT BIT(12)
+#define PIN_PU_PLL_BIT BIT(16)
+#define PIN_PU_RX_BIT BIT(17)
+#define PIN_PU_TX_BIT BIT(18)
+#define PIN_TX_IDLE_BIT BIT(19)
+#define GEN_RX_SEL_MASK GENMASK(25, 22)
+#define GEN_RX_SEL_VALUE(val) FIELD_PREP(GEN_RX_SEL_MASK, (val))
+#define GEN_TX_SEL_MASK GENMASK(29, 26)
+#define GEN_TX_SEL_VALUE(val) FIELD_PREP(GEN_TX_SEL_MASK, (val))
+#define SERDES_SPEED_1_25_G 0x6
+#define SERDES_SPEED_3_125_G 0x8
+#define PHY_RX_INIT_BIT BIT(30)
+
+/*
+ * lane0: USB3/GbE1 PHY Status 1
+ * lane1: PCIe/GbE0 PHY Status 1
+ * (used only by SGMII code)
+ */
+#define COMPHY_PHY_STAT1 0x18
+#define PHY_RX_INIT_DONE_BIT BIT(0)
+#define PHY_PLL_READY_RX_BIT BIT(2)
+#define PHY_PLL_READY_TX_BIT BIT(3)
+
+/* PHY Selector */
+#define COMPHY_SELECTOR_PHY_REG 0xFC
+/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */
+#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT BIT(0)
+/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */
+#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT BIT(4)
+/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */
+#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT BIT(8)
struct mvebu_a3700_comphy_conf {
unsigned int lane;
enum phy_mode mode;
int submode;
- unsigned int port;
- u32 fw_mode;
};
-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
+#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode) \
{ \
.lane = _lane, \
.mode = _mode, \
.submode = _smode, \
- .port = _port, \
- .fw_mode = _fw, \
}
-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
+#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \
+ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA)
-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
+#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \
+ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode)
static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
/* lane 0 */
- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
- COMPHY_FW_MODE_USB3H),
- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
- COMPHY_FW_MODE_SGMII),
- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
- COMPHY_FW_MODE_2500BASEX),
+ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX),
/* lane 1 */
- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
- COMPHY_FW_MODE_PCIE),
- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
- COMPHY_FW_MODE_SGMII),
- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
- COMPHY_FW_MODE_2500BASEX),
+ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX),
/* lane 2 */
- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
- COMPHY_FW_MODE_SATA),
- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
- COMPHY_FW_MODE_USB3H),
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA),
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS),
+};
+
+struct mvebu_a3700_comphy_priv {
+ void __iomem *comphy_regs;
+ void __iomem *lane0_phy_regs; /* USB3 and GbE1 */
+ void __iomem *lane1_phy_regs; /* PCIe and GbE0 */
+ void __iomem *lane2_phy_indirect; /* SATA and USB3 */
+ spinlock_t lock; /* for PHY selector access */
+ bool xtal_is_40m;
};
struct mvebu_a3700_comphy_lane {
+ struct mvebu_a3700_comphy_priv *priv;
struct device *dev;
unsigned int id;
enum phy_mode mode;
int submode;
- int port;
+ bool invert_tx;
+ bool invert_rx;
+ bool needs_reset;
+};
+
+struct gbe_phy_init_data_fix {
+ u16 addr;
+ u16 value;
};
-static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
- unsigned long mode)
+/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */
+static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = {
+ { 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 },
+ { 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 },
+ { 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 },
+ { 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC },
+ { 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 },
+ { 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 },
+ { 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 },
+ { 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 },
+ { 0x104, 0x0C10 }
+};
+
+/* 40M1G25 mode init data */
+static u16 gbe_phy_init[512] = {
+ /* 0 1 2 3 4 5 6 7 */
+ /*-----------------------------------------------------------*/
+ /* 8 9 A B C D E F */
+ 0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26, /* 00 */
+ 0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52, /* 08 */
+ 0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000, /* 10 */
+ 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF, /* 18 */
+ 0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000, /* 20 */
+ 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 28 */
+ 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */
+ 0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100, /* 38 */
+ 0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00, /* 40 */
+ 0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A, /* 48 */
+ 0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001, /* 50 */
+ 0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF, /* 58 */
+ 0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000, /* 60 */
+ 0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002, /* 68 */
+ 0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780, /* 70 */
+ 0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000, /* 78 */
+ 0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000, /* 80 */
+ 0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210, /* 88 */
+ 0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F, /* 90 */
+ 0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651, /* 98 */
+ 0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000, /* A0 */
+ 0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* A8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* B0 */
+ 0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, /* B8 */
+ 0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003, /* C0 */
+ 0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000, /* C8 */
+ 0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00, /* D0 */
+ 0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000, /* D8 */
+ 0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541, /* E0 */
+ 0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200, /* E8 */
+ 0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000, /* F0 */
+ 0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000, /* F8 */
+ 0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000, /*100 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*108 */
+ 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000, /*110 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*118 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*120 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*128 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*130 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*138 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*140 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*148 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*150 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*158 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*160 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*168 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*170 */
+ 0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000, /*178 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*180 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*188 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*190 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*198 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1F0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 /*1F8 */
+};
+
+static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask)
{
- struct arm_smccc_res res;
- s32 ret;
+ u32 val;
- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
- ret = res.a0;
+ val = readl(addr);
+ val = (val & ~mask) | (data & mask);
+ writel(val, addr);
+}
+
+static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask)
+{
+ u16 val;
+
+ val = readw(addr);
+ val = (val & ~mask) | (data & mask);
+ writew(val, addr);
+}
+
+/* Used for accessing lane 2 registers (SATA/USB3 PHY) */
+static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv,
+ u32 offset, u16 data, u16 mask)
+{
+ writel(offset,
+ priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR);
+ comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA,
+ data, mask);
+}
+
+static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane,
+ u16 reg, u16 data, u16 mask)
+{
+ if (lane->id == 2) {
+ /* lane 2 PHY registers are accessed indirectly */
+ comphy_set_indirect(lane->priv,
+ reg + COMPHY_LANE2_REGS_BASE,
+ data, mask);
+ } else {
+ void __iomem *base = lane->id == 1 ?
+ lane->priv->lane1_phy_regs :
+ lane->priv->lane0_phy_regs;
+
+ comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg),
+ data, mask);
+ }
+}
+
+static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane,
+ u16 reg, u16 bits,
+ ulong sleep_us, ulong timeout_us)
+{
+ int ret;
+
+ if (lane->id == 2) {
+ u32 data;
+
+ /* lane 2 PHY registers are accessed indirectly */
+ writel(reg + COMPHY_LANE2_REGS_BASE,
+ lane->priv->lane2_phy_indirect +
+ COMPHY_LANE2_INDIR_ADDR);
+
+ ret = readl_poll_timeout(lane->priv->lane2_phy_indirect +
+ COMPHY_LANE2_INDIR_DATA,
+ data, (data & bits) == bits,
+ sleep_us, timeout_us);
+ } else {
+ void __iomem *base = lane->id == 1 ?
+ lane->priv->lane1_phy_regs :
+ lane->priv->lane0_phy_regs;
+ u16 data;
+
+ ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg),
+ data, (data & bits) == bits,
+ sleep_us, timeout_us);
+ }
+
+ return ret;
+}
+
+static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane,
+ u8 reg, u32 data, u32 mask)
+{
+ comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg),
+ data, mask);
+}
+
+static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane,
+ u8 reg, u32 bits,
+ ulong sleep_us, ulong timeout_us)
+{
+ u32 data;
+
+ return readl_poll_timeout(lane->priv->comphy_regs +
+ COMPHY_PHY_REG(lane->id, reg),
+ data, (data & bits) == bits,
+ sleep_us, timeout_us);
+}
+
+/* PHY selector configures with corresponding modes */
+static int
+mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 old, new, clr = 0, set = 0;
+ unsigned long flags;
+
+ switch (lane->mode) {
+ case PHY_MODE_SATA:
+ /* SATA must be in Lane2 */
+ if (lane->id == 2)
+ clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ case PHY_MODE_ETHERNET:
+ if (lane->id == 0)
+ clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
+ else if (lane->id == 1)
+ clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ case PHY_MODE_USB_HOST_SS:
+ if (lane->id == 2)
+ set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
+ else if (lane->id == 0)
+ set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ case PHY_MODE_PCIE:
+ /* PCIE must be in Lane1 */
+ if (lane->id == 1)
+ set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
+ else
+ goto error;
+ break;
- switch (ret) {
- case SMCCC_RET_SUCCESS:
- return 0;
- case SMCCC_RET_NOT_SUPPORTED:
- return -EOPNOTSUPP;
default:
+ goto error;
+ }
+
+ spin_lock_irqsave(&lane->priv->lock, flags);
+
+ old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
+ new = (old & ~clr) | set;
+ writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
+
+ spin_unlock_irqrestore(&lane->priv->lock, flags);
+
+ dev_dbg(lane->dev,
+ "COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n",
+ lane->id, lane->mode, old, new);
+
+ return 0;
+error:
+ dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id,
+ lane->mode);
+ return -EINVAL;
+}
+
+static int
+mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, ref_clk;
+ int ret;
+
+ /* Configure phy selector for SATA */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /* Clear phy isolation mode to make it work in normal mode */
+ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
+ 0x0, PHY_ISOLATE_MODE);
+
+ /* 0. Check the Polarity invert bits */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /* 1. Select 40-bit data width */
+ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
+ DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK);
+
+ /* 2. Select reference clock(25M) and PHY mode (SATA) */
+ if (lane->priv->xtal_is_40m)
+ ref_clk = REF_FREF_SEL_SERDES_40MHZ;
+ else
+ ref_clk = REF_FREF_SEL_SERDES_25MHZ;
+
+ data = ref_clk | COMPHY_MODE_SATA;
+ mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /* 3. Use maximum PLL rate (no power save) */
+ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
+ USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT);
+
+ /* 4. Reset reserved bit */
+ comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG,
+ 0x0, PHYCTRL_FRM_PIN_BIT);
+
+ /* 5. Set vendor-specific configuration (It is done in sata driver) */
+ /* XXX: in U-Boot below sequence was executed in this place, in Linux
+ * not. Now it is done only in U-Boot before this comphy
+ * initialization - tests shows that it works ok, but in case of any
+ * future problem it is left for reference.
+ * reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff);
+ * reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6));
+ */
+
+ /* Wait for > 55 us to allow PLL be enabled */
+ udelay(PLL_SET_DELAY_US);
+
+ /* Polling status */
+ ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN,
+ PLL_READY_TX_BIT, COMPHY_PLL_SLEEP,
+ COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to lock SATA PLL\n");
+
+ return ret;
+}
+
+static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane,
+ bool is_1gbps)
+{
+ int addr, fix_idx;
+ u16 val;
+
+ fix_idx = 0;
+ for (addr = 0; addr < 512; addr++) {
+ /*
+ * All PHY register values are defined in full for 3.125Gbps
+ * SERDES speed. The values required for 1.25 Gbps are almost
+ * the same and only few registers should be "fixed" in
+ * comparison to 3.125 Gbps values. These register values are
+ * stored in "gbe_phy_init_fix" array.
+ */
+ if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) {
+ /* Use new value */
+ val = gbe_phy_init_fix[fix_idx].value;
+ if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix))
+ fix_idx++;
+ } else {
+ val = gbe_phy_init[addr];
+ }
+
+ comphy_lane_reg_set(lane, addr, val, 0xFFFF);
+ }
+}
+
+static int
+mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, speed_sel;
+ int ret;
+
+ /* Set selector */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /*
+ * 1. Reset PHY by setting PHY input port PIN_RESET=1.
+ * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep
+ * PHY TXP/TXN output to idle state during PHY initialization
+ * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0.
+ */
+ data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT;
+ mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT |
+ PIN_PU_TX_BIT | PHY_RX_INIT_BIT;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /* 4. Release reset to the PHY by setting PIN_RESET=0. */
+ data = 0x0;
+ mask = PIN_RESET_COMPHY_BIT;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /*
+ * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY
+ * bit rate
+ */
+ switch (lane->submode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ /* SGMII 1G, SerDes speed 1.25G */
+ speed_sel = SERDES_SPEED_1_25_G;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ /* 2500Base-X, SerDes speed 3.125G */
+ speed_sel = SERDES_SPEED_3_125_G;
+ break;
+ default:
+ /* Other rates are not supported */
+ dev_err(lane->dev,
+ "unsupported phy speed %d on comphy lane%d\n",
+ lane->submode, lane->id);
return -EINVAL;
}
+ data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel);
+ mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /*
+ * 6. Wait 10mS for bandgap and reference clocks to stabilize; then
+ * start SW programming.
+ */
+ mdelay(10);
+
+ /* 7. Program COMPHY register PHY_MODE */
+ data = COMPHY_MODE_SERDES;
+ mask = COMPHY_MODE_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /*
+ * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK
+ * source
+ */
+ data = 0x0;
+ mask = PHY_REF_CLK_SEL;
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
+
+ /*
+ * 9. Set correct reference clock frequency in COMPHY register
+ * REF_FREF_SEL.
+ */
+ if (lane->priv->xtal_is_40m)
+ data = REF_FREF_SEL_SERDES_50MHZ;
+ else
+ data = REF_FREF_SEL_SERDES_25MHZ;
+
+ mask = REF_FREF_SEL_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /*
+ * 10. Program COMPHY register PHY_GEN_MAX[1:0]
+ * This step is mentioned in the flow received from verification team.
+ * However the PHY_GEN_MAX value is only meaningful for other interfaces
+ * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or
+ * PCIe speed 2.5/5 Gbps
+ */
+
+ /*
+ * 11. Program COMPHY register SEL_BITS to set correct parallel data
+ * bus width
+ */
+ data = DATA_WIDTH_10BIT;
+ mask = SEL_DATA_WIDTH_MASK;
+ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask);
+
+ /*
+ * 12. As long as DFE function needs to be enabled in any mode,
+ * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F
+ * for real chip during COMPHY power on.
+ * The value of the DFE_UPDATE_EN already is 0x3F, because it is the
+ * default value after reset of the PHY.
+ */
+
+ /*
+ * 13. Program COMPHY GEN registers.
+ * These registers should be programmed based on the lab testing result
+ * to achieve optimal performance. Please contact the CEA group to get
+ * the related GEN table during real chip bring-up. We only required to
+ * run though the entire registers programming flow defined by
+ * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock
+ * 25 MHz the default values stored in PHY registers are OK.
+ */
+ dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n",
+ lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G");
+ if (lane->priv->xtal_is_40m)
+ comphy_gbe_phy_init(lane,
+ lane->submode != PHY_INTERFACE_MODE_2500BASEX);
+
+ /*
+ * 14. Check the PHY Polarity invert bit
+ */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /*
+ * 15. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to
+ * start PHY power up sequence. All the PHY register programming should
+ * be done before PIN_PU_PLL=1. There should be no register programming
+ * for normal PHY operation from this point.
+ */
+ data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
+ mask = data;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /*
+ * 16. Wait for PHY power up sequence to finish by checking output ports
+ * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1.
+ */
+ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
+ PHY_PLL_READY_TX_BIT |
+ PHY_PLL_READY_RX_BIT,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret) {
+ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
+ lane->id);
+ return ret;
+ }
+
+ /*
+ * 17. Set COMPHY input port PIN_TX_IDLE=0
+ */
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT);
+
+ /*
+ * 18. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To
+ * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the
+ * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to
+ * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please
+ * refer to RX initialization part for details.
+ */
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1,
+ PHY_RX_INIT_BIT, PHY_RX_INIT_BIT);
+
+ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
+ PHY_PLL_READY_TX_BIT |
+ PHY_PLL_READY_RX_BIT,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret) {
+ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
+ lane->id);
+ return ret;
+ }
+
+ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
+ PHY_RX_INIT_DONE_BIT,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n",
+ lane->id);
+
+ return ret;
+}
+
+static int
+mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, cfg, ref_clk;
+ int ret;
+
+ /* Set phy seclector */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /*
+ * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The
+ * register belong to UTMI module, so it is set in UTMI phy driver.
+ */
+
+ /*
+ * 1. Set PRD_TXDEEMPH (3.5db de-emph)
+ */
+ data = PRD_TXDEEMPH0_MASK;
+ mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK |
+ CFG_TX_ALIGN_POS_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask);
+
+ /*
+ * 2. Set BIT0: enable transmitter in high impedance mode
+ * Set BIT[3:4]: delay 2 clock cycles for HiZ off latency
+ * Set BIT6: Tx detect Rx at HiZ mode
+ * Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db
+ * together with bit 0 of COMPHY_PIPE_LANE_CFG0 register
+ */
+ data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN;
+ mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK |
+ TX_ELEC_IDLE_MODE_EN;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask);
+
+ /*
+ * 3. Set Spread Spectrum Clock Enabled
+ */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4,
+ SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN);
+
+ /*
+ * 4. Set Override Margining Controls From the MAC:
+ * Use margining signals from lane configuration
+ */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL,
+ MODE_MARGIN_OVERRIDE, 0xFFFF);
+
+ /*
+ * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles
+ * set Mode Clock Source = PCLK is generated from REFCLK
+ */
+ data = 0x0;
+ mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK |
+ BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask);
+
+ /*
+ * 6. Set G2 Spread Spectrum Clock Amplitude at 4K
+ */
+ comphy_lane_reg_set(lane, COMPHY_GEN2_SET2,
+ GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK);
+
+ /*
+ * 7. Unset G3 Spread Spectrum Clock Amplitude
+ * set G3 TX and RX Register Master Current Select
+ */
+ data = GS2_VREG_RXTX_MAS_ISET_60U;
+ mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK |
+ GS2_RSVD_6_0_MASK;
+ comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask);
+
+ /*
+ * 8. Check crystal jumper setting and program the Power and PLL Control
+ * accordingly Change RX wait
+ */
+ if (lane->priv->xtal_is_40m) {
+ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
+ cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT;
+ } else {
+ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
+ cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT;
+ }
+
+ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
+ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk;
+ mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
+ PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK |
+ REF_FREF_SEL_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg;
+ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
+ CFG_PM_RXDLOZ_WAIT_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
+
+ /*
+ * 9. Enable idle sync
+ */
+ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
+ IDLE_SYNC_EN, IDLE_SYNC_EN);
+
+ /*
+ * 10. Enable the output of 500M clock
+ */
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN);
+
+ /*
+ * 11. Set 20-bit data width
+ */
+ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
+ DATA_WIDTH_20BIT, 0xFFFF);
+
+ /*
+ * 12. Override Speed_PLL value and use MAC PLL
+ */
+ data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT;
+ mask = 0xFFFF;
+ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask);
+
+ /*
+ * 13. Check the Polarity invert bit
+ */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /*
+ * 14. Set max speed generation to USB3.0 5Gbps
+ */
+ comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN,
+ PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK);
+
+ /*
+ * 15. Set capacitor value for FFE gain peaking to 0xF
+ */
+ comphy_lane_reg_set(lane, COMPHY_GEN2_SET3,
+ GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK);
+
+ /*
+ * 16. Release SW reset
+ */
+ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4;
+ mask = 0xFFFF;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
+
+ /* Wait for > 55 us to allow PCLK be enabled */
+ udelay(PLL_SET_DELAY_US);
+
+ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to lock USB3 PLL\n");
+
+ return ret;
+}
+
+static int
+mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, ref_clk;
+ int ret;
+
+ /* Configure phy selector for PCIe */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /* 1. Enable max PLL. */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1,
+ USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN);
+
+ /* 2. Select 20 bit SERDES interface. */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO,
+ CFG_SEL_20B, CFG_SEL_20B);
+
+ /* 3. Force to use reg setting for PCIe mode */
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1,
+ SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE);
+
+ /* 4. Change RX wait */
+ data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT;
+ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
+ CFG_PM_RXDLOZ_WAIT_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
+
+ /* 5. Enable idle sync */
+ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
+ IDLE_SYNC_EN, IDLE_SYNC_EN);
+
+ /* 6. Enable the output of 100M/125M/500M clock */
+ data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN;
+ mask = data;
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
+
+ /*
+ * 7. Enable TX, PCIE global register, 0xd0074814, it is done in
+ * PCI-E driver
+ */
+
+ /*
+ * 8. Check crystal jumper setting and program the Power and PLL
+ * Control accordingly
+ */
+
+ if (lane->priv->xtal_is_40m)
+ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
+ else
+ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
+
+ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
+ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk;
+ mask = 0xFFFF;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /* 9. Override Speed_PLL value and use MAC PLL */
+ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
+ SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT,
+ 0xFFFF);
+
+ /* 10. Check the Polarity invert bit */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /* 11. Release SW reset */
+ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32;
+ mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
+
+ /* Wait for > 55 us to allow PCLK be enabled */
+ udelay(PLL_SET_DELAY_US);
+
+ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to lock PCIE PLL\n");
+
+ return ret;
+}
+
+static void
+mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ /* Set phy isolation mode */
+ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
+ PHY_ISOLATE_MODE, PHY_ISOLATE_MODE);
+
+ /* Power off PLL, Tx, Rx */
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
+ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
+}
+
+static void
+mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data;
+
+ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT |
+ PHY_RX_INIT_BIT;
+ mask = data;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+}
+
+static void
+mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ /* Power off PLL, Tx, Rx */
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
+ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
+}
+
+static int mvebu_a3700_comphy_reset(struct phy *phy)
+{
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+ u16 mask, data;
+
+ dev_dbg(lane->dev, "resetting lane %d\n", lane->id);
+
+ /* COMPHY reset for internal logic */
+ comphy_lane_reg_set(lane, COMPHY_SFT_RESET,
+ SFT_RST_NO_REG, SFT_RST_NO_REG);
+
+ /* COMPHY register reset (cleared automatically) */
+ comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST);
+
+ /* PIPE soft and register reset */
+ data = PIPE_SOFT_RESET | PIPE_REG_RESET;
+ mask = data;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
+
+ /* Release PIPE register reset */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL,
+ 0x0, PIPE_REG_RESET);
+
+ /* Reset SB configuration register (only for lanes 0 and 1) */
+ if (lane->id == 0 || lane->id == 1) {
+ u32 mask, data;
+
+ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT |
+ PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
+ mask = data | PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+ }
+
+ return 0;
}
-static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
+static bool mvebu_a3700_comphy_check_mode(int lane,
enum phy_mode mode,
int submode)
{
@@ -128,38 +1141,40 @@ static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
/* Unused PHY mux value is 0x0 */
if (mode == PHY_MODE_INVALID)
- return -EINVAL;
+ return false;
for (i = 0; i < n; i++) {
if (mvebu_a3700_comphy_modes[i].lane == lane &&
- mvebu_a3700_comphy_modes[i].port == port &&
mvebu_a3700_comphy_modes[i].mode == mode &&
mvebu_a3700_comphy_modes[i].submode == submode)
break;
}
if (i == n)
- return -EINVAL;
+ return false;
- return mvebu_a3700_comphy_modes[i].fw_mode;
+ return true;
}
static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
- int fw_mode;
-
- if (submode == PHY_INTERFACE_MODE_1000BASEX)
- submode = PHY_INTERFACE_MODE_SGMII;
- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
- submode);
- if (fw_mode < 0) {
+ if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) {
dev_err(lane->dev, "invalid COMPHY mode\n");
- return fw_mode;
+ return -EINVAL;
}
+ /* Mode cannot be changed while the PHY is powered on */
+ if (phy->power_count &&
+ (lane->mode != mode || lane->submode != submode))
+ return -EBUSY;
+
+ /* If changing mode, ensure reset is called */
+ if (lane->mode != PHY_MODE_INVALID && lane->mode != mode)
+ lane->needs_reset = true;
+
/* Just remember the mode, ->power_on() will do the real setup */
lane->mode = mode;
lane->submode = submode;
@@ -170,75 +1185,77 @@ static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
static int mvebu_a3700_comphy_power_on(struct phy *phy)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
- u32 fw_param;
- int fw_mode;
int ret;
- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
- lane->mode, lane->submode);
- if (fw_mode < 0) {
+ if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode,
+ lane->submode)) {
dev_err(lane->dev, "invalid COMPHY mode\n");
- return fw_mode;
+ return -EINVAL;
+ }
+
+ if (lane->needs_reset) {
+ ret = mvebu_a3700_comphy_reset(phy);
+ if (ret)
+ return ret;
+
+ lane->needs_reset = false;
}
switch (lane->mode) {
case PHY_MODE_USB_HOST_SS:
dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
- fw_param = COMPHY_FW_MODE(fw_mode);
- break;
+ return mvebu_a3700_comphy_usb3_power_on(lane);
case PHY_MODE_SATA:
dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
- fw_param = COMPHY_FW_MODE(fw_mode);
- break;
+ return mvebu_a3700_comphy_sata_power_on(lane);
case PHY_MODE_ETHERNET:
- switch (lane->submode) {
- case PHY_INTERFACE_MODE_SGMII:
- dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
- lane->id);
- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
- COMPHY_FW_SPEED_1_25G);
- break;
- case PHY_INTERFACE_MODE_2500BASEX:
- dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
- lane->id);
- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
- COMPHY_FW_SPEED_3_125G);
- break;
- default:
- dev_err(lane->dev, "unsupported PHY submode (%d)\n",
- lane->submode);
- return -ENOTSUPP;
- }
- break;
+ dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id);
+ return mvebu_a3700_comphy_ethernet_power_on(lane);
case PHY_MODE_PCIE:
dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
- fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
- COMPHY_FW_SPEED_5G,
- phy->attrs.bus_width);
- break;
+ return mvebu_a3700_comphy_pcie_power_on(lane);
default:
dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
-
- ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
- if (ret == -EOPNOTSUPP)
- dev_err(lane->dev,
- "unsupported SMC call, try updating your firmware\n");
-
- return ret;
}
static int mvebu_a3700_comphy_power_off(struct phy *phy)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
- return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
+ switch (lane->mode) {
+ case PHY_MODE_USB_HOST_SS:
+ /*
+ * The USB3 MAC sets the USB3 PHY to low state, so we do not
+ * need to power off USB3 PHY again.
+ */
+ break;
+
+ case PHY_MODE_SATA:
+ mvebu_a3700_comphy_sata_power_off(lane);
+ break;
+
+ case PHY_MODE_ETHERNET:
+ mvebu_a3700_comphy_ethernet_power_off(lane);
+ break;
+
+ case PHY_MODE_PCIE:
+ mvebu_a3700_comphy_pcie_power_off(lane);
+ break;
+
+ default:
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
}
static const struct phy_ops mvebu_a3700_comphy_ops = {
.power_on = mvebu_a3700_comphy_power_on,
.power_off = mvebu_a3700_comphy_power_off,
+ .reset = mvebu_a3700_comphy_reset,
.set_mode = mvebu_a3700_comphy_set_mode,
.owner = THIS_MODULE,
};
@@ -247,25 +1264,90 @@ static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct mvebu_a3700_comphy_lane *lane;
+ unsigned int port;
struct phy *phy;
- if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
- return ERR_PTR(-EINVAL);
-
phy = of_phy_simple_xlate(dev, args);
if (IS_ERR(phy))
return phy;
lane = phy_get_drvdata(phy);
- lane->port = args->args[0];
+
+ port = args->args[0];
+ if (port != 0 && (port != 1 || lane->id != 0)) {
+ dev_err(lane->dev, "invalid port number %u\n", port);
+ return ERR_PTR(-EINVAL);
+ }
+
+ lane->invert_tx = args->args[1] & BIT(0);
+ lane->invert_rx = args->args[1] & BIT(1);
return phy;
}
static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
{
+ struct mvebu_a3700_comphy_priv *priv;
struct phy_provider *provider;
struct device_node *child;
+ struct resource *res;
+ struct clk *clk;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy");
+ priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->comphy_regs))
+ return PTR_ERR(priv->comphy_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "lane1_pcie_gbe");
+ priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->lane1_phy_regs))
+ return PTR_ERR(priv->lane1_phy_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "lane0_usb3_gbe");
+ priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->lane0_phy_regs))
+ return PTR_ERR(priv->lane0_phy_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "lane2_sata_usb3");
+ priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->lane2_phy_indirect))
+ return PTR_ERR(priv->lane2_phy_indirect);
+
+ /*
+ * Driver needs to know if reference xtal clock is 40MHz or 25MHz.
+ * Old DT bindings do not have xtal clk present. So do not fail here
+ * and expects that default 25MHz reference clock is used.
+ */
+ clk = clk_get(&pdev->dev, "xtal");
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n",
+ PTR_ERR(clk));
+ } else {
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n",
+ ret);
+ } else {
+ if (clk_get_rate(clk) == 40000000)
+ priv->xtal_is_40m = true;
+ clk_disable_unprepare(clk);
+ }
+ clk_put(clk);
+ }
+
+ dev_set_drvdata(&pdev->dev, priv);
for_each_available_child_of_node(pdev->dev.of_node, child) {
struct mvebu_a3700_comphy_lane *lane;
@@ -280,7 +1362,7 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
continue;
}
- if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
+ if (lane_id >= 3) {
dev_err(&pdev->dev, "invalid 'reg' property\n");
continue;
}
@@ -298,16 +1380,26 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
return PTR_ERR(phy);
}
+ lane->priv = priv;
lane->dev = &pdev->dev;
lane->mode = PHY_MODE_INVALID;
lane->submode = PHY_INTERFACE_MODE_NA;
lane->id = lane_id;
- lane->port = -1;
+ lane->invert_tx = false;
+ lane->invert_rx = false;
phy_set_drvdata(phy, lane);
+
+ /*
+ * To avoid relying on the bootloader/firmware configuration,
+ * power off all comphys.
+ */
+ mvebu_a3700_comphy_reset(phy);
+ lane->needs_reset = false;
}
provider = devm_of_phy_provider_register(&pdev->dev,
mvebu_a3700_comphy_xlate);
+
return PTR_ERR_OR_ZERO(provider);
}
@@ -327,5 +1419,7 @@ static struct platform_driver mvebu_a3700_comphy_driver = {
module_platform_driver(mvebu_a3700_comphy_driver);
MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
+MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
+MODULE_AUTHOR("Marek Behún <kabel@kernel.org>");
MODULE_DESCRIPTION("Common PHY driver for A3700");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c
index ccb4045685cd..929e86d6558e 100644
--- a/drivers/phy/phy-core-mipi-dphy.c
+++ b/drivers/phy/phy-core-mipi-dphy.c
@@ -64,10 +64,10 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
cfg->init = 100;
- cfg->lpx = 60000;
+ cfg->lpx = 50000;
cfg->ta_get = 5 * cfg->lpx;
cfg->ta_go = 4 * cfg->lpx;
- cfg->ta_sure = 2 * cfg->lpx;
+ cfg->ta_sure = cfg->lpx;
cfg->wakeup = 1000;
cfg->hs_clk_rate = hs_clk_rate;
diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
index a8ecd2e8442d..cacd32f6e0cc 100644
--- a/drivers/phy/qualcomm/phy-qcom-edp.c
+++ b/drivers/phy/qualcomm/phy-qcom-edp.c
@@ -335,9 +335,11 @@ static int qcom_edp_phy_power_on(struct phy *phy)
writel(0x00, edp->tx0 + TXn_LANE_MODE_1);
writel(0x00, edp->tx1 + TXn_LANE_MODE_1);
- ret = qcom_edp_configure_ssc(edp);
- if (ret)
- return ret;
+ if (edp->dp_opts.ssc) {
+ ret = qcom_edp_configure_ssc(edp);
+ if (ret)
+ return ret;
+ }
ret = qcom_edp_configure_pll(edp);
if (ret)
@@ -654,6 +656,7 @@ static int qcom_edp_phy_probe(struct platform_device *pdev)
}
static const struct of_device_id qcom_edp_phy_match_table[] = {
+ { .compatible = "qcom,sc7280-edp-phy" },
{ .compatible = "qcom,sc8180x-edp-phy" },
{ }
};
diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c
index fec1da470d26..7bacc527fbad 100644
--- a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c
+++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c
@@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+#include <linux/bitfield.h>
/* USB QSCRATCH Hardware registers */
#define QSCRATCH_GENERAL_CFG (0x08)
@@ -74,20 +75,20 @@
PHY_PARAM_CTRL1_LOS_BIAS_MASK)
#define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \
- (((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK)
+ FIELD_PREP(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK, (x))
#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \
- (((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK)
+ FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK, (x))
#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \
- (((x) << 8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK)
+ FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK, x)
#define PHY_PARAM_CTRL1_LOS_BIAS(x) \
- (((x) << 3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK)
+ FIELD_PREP(PHY_PARAM_CTRL1_LOS_BIAS_MASK, (x))
/* RX OVRD IN HI bits */
#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13)
#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12)
#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11)
#define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7)
-#define RX_OVRD_IN_HI_RX_EQ(x) ((x) << 8)
+#define RX_OVRD_IN_HI_RX_EQ(x) FIELD_PREP(RX_OVRD_IN_HI_RX_EQ_MASK, (x))
#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7)
#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6)
#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5)
@@ -111,6 +112,9 @@
#define SS_CR_READ_REG BIT(0)
#define SS_CR_WRITE_REG BIT(0)
+#define LATCH_SLEEP 40
+#define LATCH_TIMEOUT 100
+
struct usb_phy {
void __iomem *base;
struct device *dev;
@@ -156,19 +160,9 @@ static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3,
static int wait_for_latch(void __iomem *addr)
{
- u32 retry = 10;
-
- while (true) {
- if (!readl(addr))
- break;
-
- if (--retry == 0)
- return -ETIMEDOUT;
-
- usleep_range(10, 20);
- }
+ u32 val;
- return 0;
+ return readl_poll_timeout(addr, val, !val, LATCH_SLEEP, LATCH_TIMEOUT);
}
/**
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c
index 8ea87c69f463..b144ae1f729a 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.c
@@ -5978,6 +5978,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
.compatible = "qcom,sc8180x-qmp-ufs-phy",
.data = &sm8150_ufsphy_cfg,
}, {
+ .compatible = "qcom,sc8280xp-qmp-ufs-phy",
+ .data = &sm8350_ufsphy_cfg,
+ }, {
.compatible = "qcom,sc8180x-qmp-usb3-phy",
.data = &sm8150_usb3phy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index 032d02bf50c5..7529a7e6e5df 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -912,6 +912,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
.compatible = "qcom,ipq8074-qusb2-phy",
.data = &msm8996_phy_cfg,
}, {
+ .compatible = "qcom,msm8953-qusb2-phy",
+ .data = &msm8996_phy_cfg,
+ }, {
.compatible = "qcom,msm8996-qusb2-phy",
.data = &msm8996_phy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
index 7e61202aa234..5d203784f75d 100644
--- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
+++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
@@ -32,6 +32,7 @@
#define POR BIT(1)
#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)
+#define SIDDQ BIT(2)
#define RETENABLEN BIT(3)
#define FSEL_MASK GENMASK(6, 4)
#define FSEL_DEFAULT (0x3 << 4)
@@ -233,6 +234,9 @@ static int qcom_snps_hsphy_init(struct phy *phy)
qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
SLEEPM, SLEEPM);
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
+ SIDDQ, 0);
+
qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
POR, 0);
@@ -275,6 +279,7 @@ static const struct phy_ops qcom_snps_hsphy_gen_ops = {
static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
{ .compatible = "qcom,sm8150-usb-hs-phy", },
+ { .compatible = "qcom,usb-snps-hs-5nm-phy", },
{ .compatible = "qcom,usb-snps-hs-7nm-phy", },
{ .compatible = "qcom,usb-snps-femto-v2-phy", },
{ }
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index e812adad7242..9022e395c056 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -66,6 +66,14 @@ config PHY_ROCKCHIP_INNO_DSIDPHY
Enable this to support the Rockchip MIPI/LVDS/TTL PHY with
Innosilicon IP block.
+config PHY_ROCKCHIP_NANENG_COMBO_PHY
+ tristate "Rockchip NANENG COMBO PHY Driver"
+ depends on ARCH_ROCKCHIP && OF
+ select GENERIC_PHY
+ help
+ Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII
+ combo PHY with NaNeng IP block.
+
config PHY_ROCKCHIP_PCIE
tristate "Rockchip PCIe PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index f0eec212b2aa..a5041efb5b8f 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
+obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
new file mode 100644
index 000000000000..7b213825fb5d
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip PIPE USB3.0 PCIE SATA Combo Phy driver
+ *
+ * Copyright (C) 2021 Rockchip Electronics Co., Ltd.
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/units.h>
+
+#define BIT_WRITEABLE_SHIFT 16
+#define REF_CLOCK_24MHz (24 * HZ_PER_MHZ)
+#define REF_CLOCK_25MHz (25 * HZ_PER_MHZ)
+#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ)
+
+/* COMBO PHY REG */
+#define PHYREG6 0x14
+#define PHYREG6_PLL_DIV_MASK GENMASK(7, 6)
+#define PHYREG6_PLL_DIV_SHIFT 6
+#define PHYREG6_PLL_DIV_2 1
+
+#define PHYREG7 0x18
+#define PHYREG7_TX_RTERM_MASK GENMASK(7, 4)
+#define PHYREG7_TX_RTERM_SHIFT 4
+#define PHYREG7_TX_RTERM_50OHM 8
+#define PHYREG7_RX_RTERM_MASK GENMASK(3, 0)
+#define PHYREG7_RX_RTERM_SHIFT 0
+#define PHYREG7_RX_RTERM_44OHM 15
+
+#define PHYREG8 0x1C
+#define PHYREG8_SSC_EN BIT(4)
+
+#define PHYREG11 0x28
+#define PHYREG11_SU_TRIM_0_7 0xF0
+
+#define PHYREG12 0x2C
+#define PHYREG12_PLL_LPF_ADJ_VALUE 4
+
+#define PHYREG13 0x30
+#define PHYREG13_RESISTER_MASK GENMASK(5, 4)
+#define PHYREG13_RESISTER_SHIFT 0x4
+#define PHYREG13_RESISTER_HIGH_Z 3
+#define PHYREG13_CKRCV_AMP0 BIT(7)
+
+#define PHYREG14 0x34
+#define PHYREG14_CKRCV_AMP1 BIT(0)
+
+#define PHYREG15 0x38
+#define PHYREG15_CTLE_EN BIT(0)
+#define PHYREG15_SSC_CNT_MASK GENMASK(7, 6)
+#define PHYREG15_SSC_CNT_SHIFT 6
+#define PHYREG15_SSC_CNT_VALUE 1
+
+#define PHYREG16 0x3C
+#define PHYREG16_SSC_CNT_VALUE 0x5f
+
+#define PHYREG18 0x44
+#define PHYREG18_PLL_LOOP 0x32
+
+#define PHYREG32 0x7C
+#define PHYREG32_SSC_MASK GENMASK(7, 4)
+#define PHYREG32_SSC_DIR_SHIFT 4
+#define PHYREG32_SSC_UPWARD 0
+#define PHYREG32_SSC_DOWNWARD 1
+#define PHYREG32_SSC_OFFSET_SHIFT 6
+#define PHYREG32_SSC_OFFSET_500PPM 1
+
+#define PHYREG33 0x80
+#define PHYREG33_PLL_KVCO_MASK GENMASK(4, 2)
+#define PHYREG33_PLL_KVCO_SHIFT 2
+#define PHYREG33_PLL_KVCO_VALUE 2
+
+struct rockchip_combphy_priv;
+
+struct combphy_reg {
+ u16 offset;
+ u16 bitend;
+ u16 bitstart;
+ u16 disable;
+ u16 enable;
+};
+
+struct rockchip_combphy_grfcfg {
+ struct combphy_reg pcie_mode_set;
+ struct combphy_reg usb_mode_set;
+ struct combphy_reg sgmii_mode_set;
+ struct combphy_reg qsgmii_mode_set;
+ struct combphy_reg pipe_rxterm_set;
+ struct combphy_reg pipe_txelec_set;
+ struct combphy_reg pipe_txcomp_set;
+ struct combphy_reg pipe_clk_25m;
+ struct combphy_reg pipe_clk_100m;
+ struct combphy_reg pipe_phymode_sel;
+ struct combphy_reg pipe_rate_sel;
+ struct combphy_reg pipe_rxterm_sel;
+ struct combphy_reg pipe_txelec_sel;
+ struct combphy_reg pipe_txcomp_sel;
+ struct combphy_reg pipe_clk_ext;
+ struct combphy_reg pipe_sel_usb;
+ struct combphy_reg pipe_sel_qsgmii;
+ struct combphy_reg pipe_phy_status;
+ struct combphy_reg con0_for_pcie;
+ struct combphy_reg con1_for_pcie;
+ struct combphy_reg con2_for_pcie;
+ struct combphy_reg con3_for_pcie;
+ struct combphy_reg con0_for_sata;
+ struct combphy_reg con1_for_sata;
+ struct combphy_reg con2_for_sata;
+ struct combphy_reg con3_for_sata;
+ struct combphy_reg pipe_con0_for_sata;
+ struct combphy_reg pipe_xpcs_phy_ready;
+};
+
+struct rockchip_combphy_cfg {
+ const struct rockchip_combphy_grfcfg *grfcfg;
+ int (*combphy_cfg)(struct rockchip_combphy_priv *priv);
+};
+
+struct rockchip_combphy_priv {
+ u8 type;
+ void __iomem *mmio;
+ int num_clks;
+ struct clk_bulk_data *clks;
+ struct device *dev;
+ struct regmap *pipe_grf;
+ struct regmap *phy_grf;
+ struct phy *phy;
+ struct reset_control *phy_rst;
+ const struct rockchip_combphy_cfg *cfg;
+ bool enable_ssc;
+ bool ext_refclk;
+ struct clk *refclk;
+};
+
+static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv,
+ int mask, int val, int reg)
+{
+ unsigned int temp;
+
+ temp = readl(priv->mmio + reg);
+ temp = (temp & ~(mask)) | val;
+ writel(temp, priv->mmio + reg);
+}
+
+static int rockchip_combphy_param_write(struct regmap *base,
+ const struct combphy_reg *reg, bool en)
+{
+ u32 val, mask, tmp;
+
+ tmp = en ? reg->enable : reg->disable;
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+ return regmap_write(base, reg->offset, val);
+}
+
+static u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ u32 mask, val;
+
+ mask = GENMASK(cfg->pipe_phy_status.bitend,
+ cfg->pipe_phy_status.bitstart);
+
+ regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val);
+ val = (val & mask) >> cfg->pipe_phy_status.bitstart;
+
+ return val;
+}
+
+static int rockchip_combphy_init(struct phy *phy)
+{
+ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy);
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ u32 val;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable clks\n");
+ return ret;
+ }
+
+ switch (priv->type) {
+ case PHY_TYPE_PCIE:
+ case PHY_TYPE_USB3:
+ case PHY_TYPE_SATA:
+ case PHY_TYPE_SGMII:
+ case PHY_TYPE_QSGMII:
+ if (priv->cfg->combphy_cfg)
+ ret = priv->cfg->combphy_cfg(priv);
+ break;
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret) {
+ dev_err(priv->dev, "failed to init phy for phy type %x\n", priv->type);
+ goto err_clk;
+ }
+
+ ret = reset_control_deassert(priv->phy_rst);
+ if (ret)
+ goto err_clk;
+
+ if (priv->type == PHY_TYPE_USB3) {
+ ret = readx_poll_timeout_atomic(rockchip_combphy_is_ready,
+ priv, val,
+ val == cfg->pipe_phy_status.enable,
+ 10, 1000);
+ if (ret)
+ dev_warn(priv->dev, "wait phy status ready timeout\n");
+ }
+
+ return 0;
+
+err_clk:
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+
+ return ret;
+}
+
+static int rockchip_combphy_exit(struct phy *phy)
+{
+ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ reset_control_assert(priv->phy_rst);
+
+ return 0;
+}
+
+static const struct phy_ops rochchip_combphy_ops = {
+ .init = rockchip_combphy_init,
+ .exit = rockchip_combphy_exit,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *rockchip_combphy_xlate(struct device *dev, struct of_phandle_args *args)
+{
+ struct rockchip_combphy_priv *priv = dev_get_drvdata(dev);
+
+ if (args->args_count != 1) {
+ dev_err(dev, "invalid number of arguments\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (priv->type != PHY_NONE && priv->type != args->args[0])
+ dev_warn(dev, "phy type select %d overwriting type %d\n",
+ args->args[0], priv->type);
+
+ priv->type = args->args[0];
+
+ return priv->phy;
+}
+
+static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy_priv *priv)
+{
+ int i;
+
+ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
+ if (priv->num_clks < 1)
+ return -EINVAL;
+
+ priv->refclk = NULL;
+ for (i = 0; i < priv->num_clks; i++) {
+ if (!strncmp(priv->clks[i].id, "ref", 3)) {
+ priv->refclk = priv->clks[i].clk;
+ break;
+ }
+ }
+
+ if (!priv->refclk) {
+ dev_err(dev, "no refclk found\n");
+ return -EINVAL;
+ }
+
+ priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf");
+ if (IS_ERR(priv->pipe_grf)) {
+ dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n");
+ return PTR_ERR(priv->pipe_grf);
+ }
+
+ priv->phy_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-phy-grf");
+ if (IS_ERR(priv->phy_grf)) {
+ dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n");
+ return PTR_ERR(priv->phy_grf);
+ }
+
+ priv->enable_ssc = device_property_present(dev, "rockchip,enable-ssc");
+
+ priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk");
+
+ priv->phy_rst = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR(priv->phy_rst))
+ return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n");
+
+ return 0;
+}
+
+static int rockchip_combphy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct rockchip_combphy_priv *priv;
+ const struct rockchip_combphy_cfg *phy_cfg;
+ struct resource *res;
+ int ret;
+
+ phy_cfg = of_device_get_match_data(dev);
+ if (!phy_cfg) {
+ dev_err(dev, "no OF match data provided\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->dev = dev;
+ priv->type = PHY_NONE;
+ priv->cfg = phy_cfg;
+
+ ret = rockchip_combphy_parse_dt(dev, priv);
+ if (ret)
+ return ret;
+
+ ret = reset_control_assert(priv->phy_rst);
+ if (ret) {
+ dev_err(dev, "failed to reset phy\n");
+ return ret;
+ }
+
+ priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ dev_set_drvdata(dev, priv);
+ phy_set_drvdata(priv->phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, rockchip_combphy_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ unsigned long rate;
+ u32 val;
+
+ switch (priv->type) {
+ case PHY_TYPE_PCIE:
+ /* Set SSC downward spread spectrum. */
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK,
+ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT,
+ PHYREG32);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
+ break;
+
+ case PHY_TYPE_USB3:
+ /* Set SSC downward spread spectrum. */
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK,
+ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT,
+ PHYREG32);
+
+ /* Enable adaptive CTLE for USB3.0 Rx. */
+ val = readl(priv->mmio + PHYREG15);
+ val |= PHYREG15_CTLE_EN;
+ writel(val, priv->mmio + PHYREG15);
+
+ /* Set PLL KVCO fine tuning signals. */
+ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
+ PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT,
+ PHYREG33);
+
+ /* Enable controlling random jitter. */
+ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12);
+
+ /* Set PLL input clock divider 1/2. */
+ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK,
+ PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT,
+ PHYREG6);
+
+ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
+ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true);
+ break;
+
+ case PHY_TYPE_SATA:
+ /* Enable adaptive CTLE for SATA Rx. */
+ val = readl(priv->mmio + PHYREG15);
+ val |= PHYREG15_CTLE_EN;
+ writel(val, priv->mmio + PHYREG15);
+ /*
+ * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA.
+ * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm)
+ */
+ val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT;
+ val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT;
+ writel(val, priv->mmio + PHYREG7);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_sata, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_sata, true);
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true);
+ break;
+
+ case PHY_TYPE_SGMII:
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->sgmii_mode_set, true);
+ break;
+
+ case PHY_TYPE_QSGMII:
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_rate_sel, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true);
+ break;
+
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ rate = clk_get_rate(priv->refclk);
+
+ switch (rate) {
+ case REF_CLOCK_24MHz:
+ if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) {
+ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */
+ val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK,
+ val, PHYREG15);
+
+ writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16);
+ }
+ break;
+
+ case REF_CLOCK_25MHz:
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
+ break;
+
+ case REF_CLOCK_100MHz:
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
+ if (priv->type == PHY_TYPE_PCIE) {
+ /* PLL KVCO fine tuning. */
+ val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
+ val, PHYREG33);
+
+ /* Enable controlling random jitter. */
+ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12);
+
+ val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK,
+ val, PHYREG6);
+
+ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
+ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11);
+ } else if (priv->type == PHY_TYPE_SATA) {
+ /* downward spread spectrum +500ppm */
+ val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT;
+ val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32);
+ }
+ break;
+
+ default:
+ dev_err(priv->dev, "unsupported rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (priv->ext_refclk) {
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
+ if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) {
+ val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT;
+ val |= PHYREG13_CKRCV_AMP0;
+ rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13);
+
+ val = readl(priv->mmio + PHYREG14);
+ val |= PHYREG14_CKRCV_AMP1;
+ writel(val, priv->mmio + PHYREG14);
+ }
+ }
+
+ if (priv->enable_ssc) {
+ val = readl(priv->mmio + PHYREG8);
+ val |= PHYREG8_SSC_EN;
+ writel(val, priv->mmio + PHYREG8);
+ }
+
+ return 0;
+}
+
+static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = {
+ /* pipe-phy-grf */
+ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 },
+ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 },
+ .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 },
+ .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 },
+ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 },
+ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 },
+ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 },
+ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 },
+ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 },
+ .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 },
+ .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 },
+ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 },
+ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 },
+ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 },
+ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 },
+ .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 },
+ .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 },
+ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 },
+ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 },
+ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 },
+ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 },
+ .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 },
+ .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 },
+ .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 },
+ .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 },
+ /* pipe-grf */
+ .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 },
+ .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 },
+};
+
+static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = {
+ .grfcfg = &rk3568_combphy_grfcfgs,
+ .combphy_cfg = rk3568_combphy_cfg,
+};
+
+static const struct of_device_id rockchip_combphy_of_match[] = {
+ {
+ .compatible = "rockchip,rk3568-naneng-combphy",
+ .data = &rk3568_combphy_cfgs,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match);
+
+static struct platform_driver rockchip_combphy_driver = {
+ .probe = rockchip_combphy_probe,
+ .driver = {
+ .name = "rockchip-naneng-combphy",
+ .of_match_table = rockchip_combphy_of_match,
+ },
+};
+module_platform_driver(rockchip_combphy_driver);
+
+MODULE_DESCRIPTION("Rockchip NANENG COMBPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c
index 15c1c79e5c29..a0cdbcadf09e 100644
--- a/drivers/phy/ti/phy-tusb1210.c
+++ b/drivers/phy/ti/phy-tusb1210.c
@@ -8,24 +8,93 @@
*/
#include <linux/module.h>
#include <linux/bitfield.h>
+#include <linux/delay.h>
#include <linux/ulpi/driver.h>
#include <linux/ulpi/regs.h>
#include <linux/gpio/consumer.h>
#include <linux/phy/ulpi_phy.h>
-
-#define TUSB1210_VENDOR_SPECIFIC2 0x80
-#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0)
-#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4)
-#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6)
+#include <linux/power_supply.h>
+#include <linux/workqueue.h>
+
+#define TUSB1211_POWER_CONTROL 0x3d
+#define TUSB1211_POWER_CONTROL_SET 0x3e
+#define TUSB1211_POWER_CONTROL_CLEAR 0x3f
+#define TUSB1211_POWER_CONTROL_SW_CONTROL BIT(0)
+#define TUSB1211_POWER_CONTROL_DET_COMP BIT(1)
+#define TUSB1211_POWER_CONTROL_DP_VSRC_EN BIT(6)
+
+#define TUSB1210_VENDOR_SPECIFIC2 0x80
+#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0)
+#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4)
+#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6)
+
+#define TUSB1211_VENDOR_SPECIFIC3 0x85
+#define TUSB1211_VENDOR_SPECIFIC3_SET 0x86
+#define TUSB1211_VENDOR_SPECIFIC3_CLEAR 0x87
+#define TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET BIT(4)
+#define TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN BIT(6)
+
+#define TUSB1210_RESET_TIME_MS 50
+
+#define TUSB1210_CHG_DET_MAX_RETRIES 5
+
+/* TUSB1210 charger detection work states */
+enum tusb1210_chg_det_state {
+ TUSB1210_CHG_DET_CONNECTING,
+ TUSB1210_CHG_DET_START_DET,
+ TUSB1210_CHG_DET_READ_DET,
+ TUSB1210_CHG_DET_FINISH_DET,
+ TUSB1210_CHG_DET_CONNECTED,
+ TUSB1210_CHG_DET_DISCONNECTING,
+ TUSB1210_CHG_DET_DISCONNECTING_DONE,
+ TUSB1210_CHG_DET_DISCONNECTED,
+};
struct tusb1210 {
struct ulpi *ulpi;
struct phy *phy;
struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_cs;
+ u8 otg_ctrl;
u8 vendor_specific2;
+#ifdef CONFIG_POWER_SUPPLY
+ enum power_supply_usb_type chg_type;
+ enum tusb1210_chg_det_state chg_det_state;
+ int chg_det_retries;
+ struct delayed_work chg_det_work;
+ struct notifier_block psy_nb;
+ struct power_supply *psy;
+ struct power_supply *charger;
+#endif
};
+static int tusb1210_ulpi_write(struct tusb1210 *tusb, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = ulpi_write(tusb->ulpi, reg, val);
+ if (ret)
+ dev_err(&tusb->ulpi->dev, "error %d writing val 0x%02x to reg 0x%02x\n",
+ ret, val, reg);
+
+ return ret;
+}
+
+static int tusb1210_ulpi_read(struct tusb1210 *tusb, u8 reg, u8 *val)
+{
+ int ret;
+
+ ret = ulpi_read(tusb->ulpi, reg);
+ if (ret >= 0) {
+ *val = ret;
+ ret = 0;
+ } else {
+ dev_err(&tusb->ulpi->dev, "error %d reading reg 0x%02x\n", ret, reg);
+ }
+
+ return ret;
+}
+
static int tusb1210_power_on(struct phy *phy)
{
struct tusb1210 *tusb = phy_get_drvdata(phy);
@@ -33,12 +102,11 @@ static int tusb1210_power_on(struct phy *phy)
gpiod_set_value_cansleep(tusb->gpio_reset, 1);
gpiod_set_value_cansleep(tusb->gpio_cs, 1);
- /* Restore the optional eye diagram optimization value */
- if (tusb->vendor_specific2)
- ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2,
- tusb->vendor_specific2);
+ msleep(TUSB1210_RESET_TIME_MS);
- return 0;
+ /* Restore the optional eye diagram optimization value */
+ return tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2,
+ tusb->vendor_specific2);
}
static int tusb1210_power_off(struct phy *phy)
@@ -55,35 +123,357 @@ static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct tusb1210 *tusb = phy_get_drvdata(phy);
int ret;
+ u8 reg;
- ret = ulpi_read(tusb->ulpi, ULPI_OTG_CTRL);
+ ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &reg);
if (ret < 0)
return ret;
switch (mode) {
case PHY_MODE_USB_HOST:
- ret |= (ULPI_OTG_CTRL_DRVVBUS_EXT
+ reg |= (ULPI_OTG_CTRL_DRVVBUS_EXT
| ULPI_OTG_CTRL_ID_PULLUP
| ULPI_OTG_CTRL_DP_PULLDOWN
| ULPI_OTG_CTRL_DM_PULLDOWN);
- ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret);
- ret |= ULPI_OTG_CTRL_DRVVBUS;
+ tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
+ reg |= ULPI_OTG_CTRL_DRVVBUS;
break;
case PHY_MODE_USB_DEVICE:
- ret &= ~(ULPI_OTG_CTRL_DRVVBUS
+ reg &= ~(ULPI_OTG_CTRL_DRVVBUS
| ULPI_OTG_CTRL_DP_PULLDOWN
| ULPI_OTG_CTRL_DM_PULLDOWN);
- ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret);
- ret &= ~ULPI_OTG_CTRL_DRVVBUS_EXT;
+ tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
+ reg &= ~ULPI_OTG_CTRL_DRVVBUS_EXT;
break;
default:
/* nothing */
return 0;
}
- return ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret);
+ tusb->otg_ctrl = reg;
+ return tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
}
+#ifdef CONFIG_POWER_SUPPLY
+const char * const tusb1210_chg_det_states[] = {
+ "CHG_DET_CONNECTING",
+ "CHG_DET_START_DET",
+ "CHG_DET_READ_DET",
+ "CHG_DET_FINISH_DET",
+ "CHG_DET_CONNECTED",
+ "CHG_DET_DISCONNECTING",
+ "CHG_DET_DISCONNECTING_DONE",
+ "CHG_DET_DISCONNECTED",
+};
+
+static void tusb1210_reset(struct tusb1210 *tusb)
+{
+ gpiod_set_value_cansleep(tusb->gpio_reset, 0);
+ usleep_range(200, 500);
+ gpiod_set_value_cansleep(tusb->gpio_reset, 1);
+}
+
+static void tusb1210_chg_det_set_type(struct tusb1210 *tusb,
+ enum power_supply_usb_type type)
+{
+ dev_dbg(&tusb->ulpi->dev, "charger type: %d\n", type);
+ tusb->chg_type = type;
+ tusb->chg_det_retries = 0;
+ power_supply_changed(tusb->psy);
+}
+
+static void tusb1210_chg_det_set_state(struct tusb1210 *tusb,
+ enum tusb1210_chg_det_state new_state,
+ int delay_ms)
+{
+ if (delay_ms)
+ dev_dbg(&tusb->ulpi->dev, "chg_det new state %s in %d ms\n",
+ tusb1210_chg_det_states[new_state], delay_ms);
+
+ tusb->chg_det_state = new_state;
+ mod_delayed_work(system_long_wq, &tusb->chg_det_work,
+ msecs_to_jiffies(delay_ms));
+}
+
+static void tusb1210_chg_det_handle_ulpi_error(struct tusb1210 *tusb)
+{
+ tusb1210_reset(tusb);
+ if (tusb->chg_det_retries < TUSB1210_CHG_DET_MAX_RETRIES) {
+ tusb->chg_det_retries++;
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET,
+ TUSB1210_RESET_TIME_MS);
+ } else {
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET,
+ TUSB1210_RESET_TIME_MS);
+ }
+}
+
+/*
+ * Boards using a TUSB121x for charger-detection have 3 power_supply class devs:
+ *
+ * tusb1211-charger-detect(1) -> charger -> fuel-gauge
+ *
+ * To determine if an USB charger is connected to the board, the online prop of
+ * the charger psy needs to be read. Since the tusb1211-charger-detect psy is
+ * the start of the supplier -> supplied-to chain, power_supply_am_i_supplied()
+ * cannot be used here.
+ *
+ * Instead, below is a list of the power_supply names of known chargers for
+ * these boards and the charger psy is looked up by name from this list.
+ *
+ * (1) modelling the external USB charger
+ */
+static const char * const tusb1210_chargers[] = {
+ "bq24190-charger",
+};
+
+static bool tusb1210_get_online(struct tusb1210 *tusb)
+{
+ union power_supply_propval val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tusb1210_chargers) && !tusb->charger; i++)
+ tusb->charger = power_supply_get_by_name(tusb1210_chargers[i]);
+
+ if (!tusb->charger)
+ return false;
+
+ if (power_supply_get_property(tusb->charger, POWER_SUPPLY_PROP_ONLINE, &val))
+ return false;
+
+ return val.intval;
+}
+
+static void tusb1210_chg_det_work(struct work_struct *work)
+{
+ struct tusb1210 *tusb = container_of(work, struct tusb1210, chg_det_work.work);
+ bool vbus_present = tusb1210_get_online(tusb);
+ int ret;
+ u8 val;
+
+ dev_dbg(&tusb->ulpi->dev, "chg_det state %s vbus_present %d\n",
+ tusb1210_chg_det_states[tusb->chg_det_state], vbus_present);
+
+ switch (tusb->chg_det_state) {
+ case TUSB1210_CHG_DET_CONNECTING:
+ tusb->chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+ tusb->chg_det_retries = 0;
+ /* Power on USB controller for ulpi_read()/_write() */
+ ret = pm_runtime_resume_and_get(tusb->ulpi->dev.parent);
+ if (ret < 0) {
+ dev_err(&tusb->ulpi->dev, "error %d runtime-resuming\n", ret);
+ /* Should never happen, skip charger detection */
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0);
+ return;
+ }
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 0);
+ break;
+ case TUSB1210_CHG_DET_START_DET:
+ /*
+ * Use the builtin charger detection FSM to keep things simple.
+ * This only detects DCP / SDP. This is good enough for the few
+ * boards which actually rely on the phy for charger detection.
+ */
+ mutex_lock(&tusb->phy->mutex);
+ ret = tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_SET,
+ TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET);
+ mutex_unlock(&tusb->phy->mutex);
+ if (ret) {
+ tusb1210_chg_det_handle_ulpi_error(tusb);
+ break;
+ }
+
+ /* Wait 400 ms for the charger detection FSM to finish */
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_READ_DET, 400);
+ break;
+ case TUSB1210_CHG_DET_READ_DET:
+ mutex_lock(&tusb->phy->mutex);
+ ret = tusb1210_ulpi_read(tusb, TUSB1211_POWER_CONTROL, &val);
+ mutex_unlock(&tusb->phy->mutex);
+ if (ret) {
+ tusb1210_chg_det_handle_ulpi_error(tusb);
+ break;
+ }
+
+ if (val & TUSB1211_POWER_CONTROL_DET_COMP)
+ tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_DCP);
+ else
+ tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_SDP);
+
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 0);
+ break;
+ case TUSB1210_CHG_DET_FINISH_DET:
+ mutex_lock(&tusb->phy->mutex);
+
+ /* Set SW_CONTROL to stop the charger-det FSM */
+ ret = tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_SET,
+ TUSB1211_POWER_CONTROL_SW_CONTROL);
+
+ /* Clear DP_VSRC_EN which may have been enabled by the charger-det FSM */
+ ret |= tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_CLEAR,
+ TUSB1211_POWER_CONTROL_DP_VSRC_EN);
+
+ /* Clear CHGD_IDP_SRC_EN (may have been enabled by the charger-det FSM) */
+ ret |= tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_CLEAR,
+ TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN);
+
+ /* If any of the above fails reset the phy */
+ if (ret) {
+ tusb1210_reset(tusb);
+ msleep(TUSB1210_RESET_TIME_MS);
+ }
+
+ /* Restore phy-parameters and OTG_CTRL register */
+ tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, tusb->otg_ctrl);
+ tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2,
+ tusb->vendor_specific2);
+
+ mutex_unlock(&tusb->phy->mutex);
+
+ pm_runtime_put(tusb->ulpi->dev.parent);
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0);
+ break;
+ case TUSB1210_CHG_DET_CONNECTED:
+ if (!vbus_present)
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING, 0);
+ break;
+ case TUSB1210_CHG_DET_DISCONNECTING:
+ /*
+ * The phy seems to take approx. 600ms longer then the charger
+ * chip (which is used to get vbus_present) to determine Vbus
+ * session end. Wait 800ms to ensure the phy has detected and
+ * signalled Vbus session end.
+ */
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING_DONE, 800);
+ break;
+ case TUSB1210_CHG_DET_DISCONNECTING_DONE:
+ /*
+ * The phy often stops reacting to ulpi_read()/_write requests
+ * after a Vbus-session end. Reset it to work around this.
+ */
+ tusb1210_reset(tusb);
+ tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_UNKNOWN);
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTED, 0);
+ break;
+ case TUSB1210_CHG_DET_DISCONNECTED:
+ if (vbus_present)
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTING, 0);
+ break;
+ }
+}
+
+static int tusb1210_psy_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct tusb1210 *tusb = container_of(nb, struct tusb1210, psy_nb);
+ struct power_supply *psy = ptr;
+
+ if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB)
+ queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0);
+
+ return NOTIFY_OK;
+}
+
+static int tusb1210_psy_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct tusb1210 *tusb = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = tusb1210_get_online(tusb);
+ break;
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = tusb->chg_type;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if (tusb->chg_type == POWER_SUPPLY_USB_TYPE_DCP)
+ val->intval = 2000000;
+ else
+ val->intval = 500000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const enum power_supply_usb_type tusb1210_psy_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_SDP,
+ POWER_SUPPLY_USB_TYPE_DCP,
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
+};
+
+static const enum power_supply_property tusb1210_psy_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static const struct power_supply_desc tusb1210_psy_desc = {
+ .name = "tusb1211-charger-detect",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .usb_types = tusb1210_psy_usb_types,
+ .num_usb_types = ARRAY_SIZE(tusb1210_psy_usb_types),
+ .properties = tusb1210_psy_props,
+ .num_properties = ARRAY_SIZE(tusb1210_psy_props),
+ .get_property = tusb1210_psy_get_prop,
+};
+
+/* Setup charger detection if requested, on errors continue without chg-det */
+static void tusb1210_probe_charger_detect(struct tusb1210 *tusb)
+{
+ struct power_supply_config psy_cfg = { .drv_data = tusb };
+ struct device *dev = &tusb->ulpi->dev;
+ int ret;
+
+ if (!device_property_read_bool(dev->parent, "linux,phy_charger_detect"))
+ return;
+
+ if (tusb->ulpi->id.product != 0x1508) {
+ dev_err(dev, "error charger detection is only supported on the TUSB1211\n");
+ return;
+ }
+
+ ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &tusb->otg_ctrl);
+ if (ret)
+ return;
+
+ tusb->psy = power_supply_register(dev, &tusb1210_psy_desc, &psy_cfg);
+ if (IS_ERR(tusb->psy))
+ return;
+
+ /*
+ * Delay initial run by 2 seconds to allow the charger driver,
+ * which is used to determine vbus_present, to load.
+ */
+ tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED;
+ INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work);
+ queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ);
+
+ tusb->psy_nb.notifier_call = tusb1210_psy_notifier;
+ power_supply_reg_notifier(&tusb->psy_nb);
+}
+
+static void tusb1210_remove_charger_detect(struct tusb1210 *tusb)
+{
+
+ if (!IS_ERR_OR_NULL(tusb->psy)) {
+ power_supply_unreg_notifier(&tusb->psy_nb);
+ cancel_delayed_work_sync(&tusb->chg_det_work);
+ power_supply_unregister(tusb->psy);
+ }
+
+ if (tusb->charger)
+ power_supply_put(tusb->charger);
+}
+#else
+static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) { }
+static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) { }
+#endif
+
static const struct phy_ops phy_ops = {
.power_on = tusb1210_power_on,
.power_off = tusb1210_power_off,
@@ -95,11 +485,14 @@ static int tusb1210_probe(struct ulpi *ulpi)
{
struct tusb1210 *tusb;
u8 val, reg;
+ int ret;
tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL);
if (!tusb)
return -ENOMEM;
+ tusb->ulpi = ulpi;
+
tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(tusb->gpio_reset))
@@ -119,7 +512,9 @@ static int tusb1210_probe(struct ulpi *ulpi)
* diagram optimization and DP/DM swap.
*/
- reg = ulpi_read(ulpi, TUSB1210_VENDOR_SPECIFIC2);
+ ret = tusb1210_ulpi_read(tusb, TUSB1210_VENDOR_SPECIFIC2, &reg);
+ if (ret)
+ return ret;
/* High speed output drive strength configuration */
if (!device_property_read_u8(&ulpi->dev, "ihstx", &val))
@@ -133,15 +528,18 @@ static int tusb1210_probe(struct ulpi *ulpi)
if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val))
u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK);
- ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg);
+ ret = tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, reg);
+ if (ret)
+ return ret;
+
tusb->vendor_specific2 = reg;
+ tusb1210_probe_charger_detect(tusb);
+
tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
if (IS_ERR(tusb->phy))
return PTR_ERR(tusb->phy);
- tusb->ulpi = ulpi;
-
phy_set_drvdata(tusb->phy, tusb);
ulpi_set_drvdata(ulpi, tusb);
return 0;
@@ -152,6 +550,7 @@ static void tusb1210_remove(struct ulpi *ulpi)
struct tusb1210 *tusb = ulpi_get_drvdata(ulpi);
ulpi_phy_destroy(ulpi, tusb->phy);
+ tusb1210_remove_charger_detect(tusb);
}
#define TI_VENDOR_ID 0x0451
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index b67539f9848c..7737d56191d7 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -896,11 +896,9 @@ static int goldfish_pipe_probe(struct platform_device *pdev)
return -EINVAL;
}
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!r)
- return -EINVAL;
-
- dev->irq = r->start;
+ dev->irq = platform_get_irq(pdev, 0);
+ if (dev->irq < 0)
+ return dev->irq;
/*
* Exchange the versions with the host device
diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c
index 35799e6401c9..2f4b11b4dfcd 100644
--- a/drivers/pps/clients/pps-gpio.c
+++ b/drivers/pps/clients/pps-gpio.c
@@ -169,7 +169,7 @@ static int pps_gpio_probe(struct platform_device *pdev)
/* GPIO setup */
ret = pps_gpio_setup(dev);
if (ret)
- return -EINVAL;
+ return ret;
/* IRQ setup */
ret = gpiod_to_irq(data->gpio_pin);
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c
index 6a1af7664f3b..b3e084b75c23 100644
--- a/drivers/pps/generators/pps_gen_parport.c
+++ b/drivers/pps/generators/pps_gen_parport.c
@@ -20,8 +20,6 @@
#include <linux/hrtimer.h>
#include <linux/parport.h>
-#define DRVDESC "parallel port PPS signal generator"
-
#define SIGNAL 0
#define NO_SIGNAL PARPORT_CONTROL_STROBE
@@ -180,6 +178,11 @@ static void parport_attach(struct parport *port)
{
struct pardev_cb pps_cb;
+ if (send_delay > SEND_DELAY_MAX) {
+ pr_err("delay value should be not greater then %d\n", SEND_DELAY_MAX);
+ return;
+ }
+
if (attached) {
/* we already have a port */
return;
@@ -231,39 +234,8 @@ static struct parport_driver pps_gen_parport_driver = {
.detach = parport_detach,
.devmodel = true,
};
-
-/* module staff */
-
-static int __init pps_gen_parport_init(void)
-{
- int ret;
-
- pr_info(DRVDESC "\n");
-
- if (send_delay > SEND_DELAY_MAX) {
- pr_err("delay value should be not greater"
- " then %d\n", SEND_DELAY_MAX);
- return -EINVAL;
- }
-
- ret = parport_register_driver(&pps_gen_parport_driver);
- if (ret) {
- pr_err("unable to register with parport\n");
- return ret;
- }
-
- return 0;
-}
-
-static void __exit pps_gen_parport_exit(void)
-{
- parport_unregister_driver(&pps_gen_parport_driver);
- pr_info("hrtimer avg error is %ldns\n", hrtimer_error);
-}
-
-module_init(pps_gen_parport_init);
-module_exit(pps_gen_parport_exit);
+module_parport_driver(pps_gen_parport_driver);
MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
-MODULE_DESCRIPTION(DRVDESC);
+MODULE_DESCRIPTION("parallel port PPS signal generator");
MODULE_LICENSE("GPL");
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 6f8ba0ddc05f..b496028b6bfa 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -256,6 +256,19 @@ config RESET_TI_SYSCON
you wish to use the reset framework for such memory-mapped devices,
say Y here. Otherwise, say N.
+config RESET_TN48M_CPLD
+ tristate "Delta Networks TN48M switch CPLD reset controller"
+ depends on MFD_TN48M_CPLD || COMPILE_TEST
+ default MFD_TN48M_CPLD
+ help
+ This enables the reset controller driver for the Delta TN48M CPLD.
+ It provides reset signals for Armada 7040 and 385 SoC-s, Alleycat 3X
+ switch MAC-s, Alaska OOB ethernet PHY, Quad Alaska ethernet PHY-s and
+ Microchip PD69200 PoE PSE controller.
+
+ This driver can also be built as a module. If so, the module will be
+ called reset-tn48m.
+
config RESET_UNIPHIER
tristate "Reset controller driver for UniPhier SoCs"
depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index bd0a97be18b5..a80a9c4008a7 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
+obj-$(CONFIG_RESET_TN48M_CPLD) += reset-tn48m.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
diff --git a/drivers/reset/reset-tn48m.c b/drivers/reset/reset-tn48m.c
new file mode 100644
index 000000000000..130027291b6e
--- /dev/null
+++ b/drivers/reset/reset-tn48m.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Delta TN48M CPLD reset driver
+ *
+ * Copyright (C) 2021 Sartura Ltd.
+ *
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/delta,tn48m-reset.h>
+
+#define TN48M_RESET_REG 0x10
+
+#define TN48M_RESET_TIMEOUT_US 125000
+#define TN48M_RESET_SLEEP_US 10
+
+struct tn48_reset_map {
+ u8 bit;
+};
+
+struct tn48_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+static const struct tn48_reset_map tn48m_resets[] = {
+ [CPU_88F7040_RESET] = {0},
+ [CPU_88F6820_RESET] = {1},
+ [MAC_98DX3265_RESET] = {2},
+ [PHY_88E1680_RESET] = {4},
+ [PHY_88E1512_RESET] = {6},
+ [POE_RESET] = {7},
+};
+
+static inline struct tn48_reset_data *to_tn48_reset_data(
+ struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct tn48_reset_data, rcdev);
+}
+
+static int tn48m_control_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct tn48_reset_data *data = to_tn48_reset_data(rcdev);
+ unsigned int val;
+
+ regmap_update_bits(data->regmap, TN48M_RESET_REG,
+ BIT(tn48m_resets[id].bit), 0);
+
+ return regmap_read_poll_timeout(data->regmap,
+ TN48M_RESET_REG,
+ val,
+ val & BIT(tn48m_resets[id].bit),
+ TN48M_RESET_SLEEP_US,
+ TN48M_RESET_TIMEOUT_US);
+}
+
+static int tn48m_control_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct tn48_reset_data *data = to_tn48_reset_data(rcdev);
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, TN48M_RESET_REG, &regval);
+ if (ret < 0)
+ return ret;
+
+ if (BIT(tn48m_resets[id].bit) & regval)
+ return 0;
+ else
+ return 1;
+}
+
+static const struct reset_control_ops tn48_reset_ops = {
+ .reset = tn48m_control_reset,
+ .status = tn48m_control_status,
+};
+
+static int tn48m_reset_probe(struct platform_device *pdev)
+{
+ struct tn48_reset_data *data;
+ struct regmap *regmap;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = regmap;
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.ops = &tn48_reset_ops;
+ data->rcdev.nr_resets = ARRAY_SIZE(tn48m_resets);
+ data->rcdev.of_node = pdev->dev.of_node;
+
+ return devm_reset_controller_register(&pdev->dev, &data->rcdev);
+}
+
+static const struct of_device_id tn48m_reset_of_match[] = {
+ { .compatible = "delta,tn48m-reset" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tn48m_reset_of_match);
+
+static struct platform_driver tn48m_reset_driver = {
+ .driver = {
+ .name = "delta-tn48m-reset",
+ .of_match_table = tn48m_reset_of_match,
+ },
+ .probe = tn48m_reset_probe,
+};
+module_platform_driver(tn48m_reset_driver);
+
+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
+MODULE_DESCRIPTION("Delta TN48M CPLD reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 67369e941d0d..354d3f89366f 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -1749,8 +1749,11 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
continue;
if (status[i] == SDW_SLAVE_UNATTACHED &&
- slave->status != SDW_SLAVE_UNATTACHED)
+ slave->status != SDW_SLAVE_UNATTACHED) {
+ dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n",
+ i, slave->status);
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
+ }
}
if (status[0] == SDW_SLAVE_ATTACHED) {
@@ -1785,6 +1788,9 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
if (slave->status == SDW_SLAVE_UNATTACHED)
break;
+ dev_warn(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n",
+ i, slave->status);
+
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
break;
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 122f7a29d8ca..63101f1ba271 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -448,8 +448,8 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
/* Clear wake status */
wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
- wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
- intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts);
+ wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id);
+ intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts);
}
mutex_unlock(sdw->link_res->shim_lock);
}
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c
index d99807765dfe..824f4f32d4dc 100644
--- a/drivers/soundwire/intel_init.c
+++ b/drivers/soundwire/intel_init.c
@@ -180,7 +180,8 @@ static struct sdw_intel_ctx
if (!res)
return NULL;
- if (acpi_bus_get_device(res->handle, &adev))
+ adev = acpi_fetch_acpi_dev(res->handle);
+ if (!adev)
return NULL;
if (!res->count)
@@ -294,13 +295,13 @@ err:
static int
sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
{
- struct acpi_device *adev;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(ctx->handle);
struct sdw_intel_link_dev *ldev;
u32 caps;
u32 link_mask;
int i;
- if (acpi_bus_get_device(ctx->handle, &adev))
+ if (!adev)
return -EINVAL;
/* Check SNDWLCAP.LCOUNT */
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index 54813417ef8e..da1ad7ebb1aa 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -11,8 +11,10 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/pm_wakeirq.h>
#include <linux/slimbus.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
@@ -20,6 +22,9 @@
#include <sound/soc.h>
#include "bus.h"
+#define SWRM_COMP_SW_RESET 0x008
+#define SWRM_COMP_STATUS 0x014
+#define SWRM_FRM_GEN_ENABLED BIT(0)
#define SWRM_COMP_HW_VERSION 0x00
#define SWRM_COMP_CFG_ADDR 0x04
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1)
@@ -29,6 +34,7 @@
#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(19, 15)
#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0)
#define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5)
+#define SWRM_COMP_MASTER_ID 0x104
#define SWRM_INTERRUPT_STATUS 0x200
#define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0)
#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0)
@@ -111,6 +117,13 @@
#define SWR_MAX_CMD_ID 14
#define MAX_FIFO_RD_RETRY 3
#define SWR_OVERFLOW_RETRY_COUNT 30
+#define SWRM_LINK_STATUS_RETRY_CNT 100
+
+enum {
+ MASTER_ID_WSA = 1,
+ MASTER_ID_RX,
+ MASTER_ID_TX
+};
struct qcom_swrm_port_config {
u8 si;
@@ -142,6 +155,7 @@ struct qcom_swrm_ctrl {
u8 rd_cmd_id;
int irq;
unsigned int version;
+ int wake_irq;
int num_din_ports;
int num_dout_ports;
int cols_index;
@@ -159,6 +173,7 @@ struct qcom_swrm_ctrl {
u32 slave_status;
u32 wr_fifo_depth;
u32 rd_fifo_depth;
+ bool clock_stop_not_supported;
};
struct qcom_swrm_data {
@@ -166,12 +181,12 @@ struct qcom_swrm_data {
u32 default_rows;
};
-static struct qcom_swrm_data swrm_v1_3_data = {
+static const struct qcom_swrm_data swrm_v1_3_data = {
.default_rows = 48,
.default_cols = 16,
};
-static struct qcom_swrm_data swrm_v1_5_data = {
+static const struct qcom_swrm_data swrm_v1_5_data = {
.default_rows = 50,
.default_cols = 16,
};
@@ -490,6 +505,30 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus)
return 0;
}
+static irqreturn_t qcom_swrm_wake_irq_handler(int irq, void *dev_id)
+{
+ struct qcom_swrm_ctrl *swrm = dev_id;
+ int ret;
+
+ ret = pm_runtime_get_sync(swrm->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(swrm->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(swrm->dev);
+ }
+
+ if (swrm->wake_irq > 0) {
+ if (!irqd_irq_disabled(irq_get_irq_data(swrm->wake_irq)))
+ disable_irq_nosync(swrm->wake_irq);
+ }
+
+ pm_runtime_mark_last_busy(swrm->dev);
+ pm_runtime_put_autosuspend(swrm->dev);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)
{
struct qcom_swrm_ctrl *swrm = dev_id;
@@ -497,6 +536,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)
u32 i;
int devnum;
int ret = IRQ_HANDLED;
+ clk_prepare_enable(swrm->hclk);
swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts);
intr_sts_masked = intr_sts & swrm->intr_mask;
@@ -604,6 +644,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)
intr_sts_masked = intr_sts & swrm->intr_mask;
} while (intr_sts_masked);
+ clk_disable_unprepare(swrm->hclk);
return ret;
}
@@ -1017,6 +1058,15 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai;
int ret, i;
+ ret = pm_runtime_get_sync(ctrl->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(ctrl->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(ctrl->dev);
+ return ret;
+ }
+
sruntime = sdw_alloc_stream(dai->name);
if (!sruntime)
return -ENOMEM;
@@ -1044,6 +1094,9 @@ static void qcom_swrm_shutdown(struct snd_pcm_substream *substream,
sdw_release_stream(ctrl->sruntime[dai->id]);
ctrl->sruntime[dai->id] = NULL;
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
+
}
static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = {
@@ -1197,12 +1250,23 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl)
static int swrm_reg_show(struct seq_file *s_file, void *data)
{
struct qcom_swrm_ctrl *swrm = s_file->private;
- int reg, reg_val;
+ int reg, reg_val, ret;
+
+ ret = pm_runtime_get_sync(swrm->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(swrm->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(swrm->dev);
+ }
for (reg = 0; reg <= SWR_MSTR_MAX_REG_ADDR; reg += 4) {
swrm->reg_read(swrm, reg, &reg_val);
seq_printf(s_file, "0x%.3x: 0x%.2x\n", reg, reg_val);
}
+ pm_runtime_mark_last_busy(swrm->dev);
+ pm_runtime_put_autosuspend(swrm->dev);
+
return 0;
}
@@ -1267,6 +1331,7 @@ static int qcom_swrm_probe(struct platform_device *pdev)
ctrl->bus.ops = &qcom_swrm_ops;
ctrl->bus.port_ops = &qcom_swrm_port_ops;
ctrl->bus.compute_params = &qcom_swrm_compute_params;
+ ctrl->bus.clk_stop_timeout = 300;
ret = qcom_swrm_get_port_config(ctrl);
if (ret)
@@ -1301,6 +1366,18 @@ static int qcom_swrm_probe(struct platform_device *pdev)
goto err_clk;
}
+ ctrl->wake_irq = of_irq_get(dev->of_node, 1);
+ if (ctrl->wake_irq > 0) {
+ ret = devm_request_threaded_irq(dev, ctrl->wake_irq, NULL,
+ qcom_swrm_wake_irq_handler,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "swr_wake_irq", ctrl);
+ if (ret) {
+ dev_err(dev, "Failed to request soundwire wake irq\n");
+ goto err_init;
+ }
+ }
+
ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
if (ret) {
dev_err(dev, "Failed to register Soundwire controller (%d)\n",
@@ -1319,6 +1396,21 @@ static int qcom_swrm_probe(struct platform_device *pdev)
(ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff,
ctrl->version & 0xffff);
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /* Clk stop is not supported on WSA Soundwire masters */
+ if (ctrl->version <= 0x01030000) {
+ ctrl->clock_stop_not_supported = true;
+ } else {
+ ctrl->reg_read(ctrl, SWRM_COMP_MASTER_ID, &val);
+ if (val == MASTER_ID_WSA)
+ ctrl->clock_stop_not_supported = true;
+ }
+
#ifdef CONFIG_DEBUG_FS
ctrl->debugfs = debugfs_create_dir("qualcomm-sdw", ctrl->bus.debugfs);
debugfs_create_file("qualcomm-registers", 0400, ctrl->debugfs, ctrl,
@@ -1345,6 +1437,115 @@ static int qcom_swrm_remove(struct platform_device *pdev)
return 0;
}
+static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *swrm)
+{
+ int retry = SWRM_LINK_STATUS_RETRY_CNT;
+ int comp_sts;
+
+ do {
+ swrm->reg_read(swrm, SWRM_COMP_STATUS, &comp_sts);
+
+ if (comp_sts & SWRM_FRM_GEN_ENABLED)
+ return true;
+
+ usleep_range(500, 510);
+ } while (retry--);
+
+ dev_err(swrm->dev, "%s: link status not %s\n", __func__,
+ comp_sts && SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected");
+
+ return false;
+}
+
+static int __maybe_unused swrm_runtime_resume(struct device *dev)
+{
+ struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev);
+ int ret;
+
+ if (ctrl->wake_irq > 0) {
+ if (!irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq)))
+ disable_irq_nosync(ctrl->wake_irq);
+ }
+
+ clk_prepare_enable(ctrl->hclk);
+
+ if (ctrl->clock_stop_not_supported) {
+ reinit_completion(&ctrl->enumeration);
+ ctrl->reg_write(ctrl, SWRM_COMP_SW_RESET, 0x01);
+ usleep_range(100, 105);
+
+ qcom_swrm_init(ctrl);
+
+ usleep_range(100, 105);
+ if (!swrm_wait_for_frame_gen_enabled(ctrl))
+ dev_err(ctrl->dev, "link failed to connect\n");
+
+ /* wait for hw enumeration to complete */
+ wait_for_completion_timeout(&ctrl->enumeration,
+ msecs_to_jiffies(TIMEOUT_MS));
+ qcom_swrm_get_device_status(ctrl);
+ sdw_handle_slave_status(&ctrl->bus, ctrl->status);
+ } else {
+ ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START);
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR,
+ SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET);
+
+ ctrl->intr_mask |= SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET;
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask);
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask);
+
+ usleep_range(100, 105);
+ if (!swrm_wait_for_frame_gen_enabled(ctrl))
+ dev_err(ctrl->dev, "link failed to connect\n");
+
+ ret = sdw_bus_exit_clk_stop(&ctrl->bus);
+ if (ret < 0)
+ dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused swrm_runtime_suspend(struct device *dev)
+{
+ struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev);
+ int ret;
+
+ if (!ctrl->clock_stop_not_supported) {
+ /* Mask bus clash interrupt */
+ ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET;
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask);
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask);
+ /* Prepare slaves for clock stop */
+ ret = sdw_bus_prep_clk_stop(&ctrl->bus);
+ if (ret < 0 && ret != -ENODATA) {
+ dev_err(dev, "prepare clock stop failed %d", ret);
+ return ret;
+ }
+
+ ret = sdw_bus_clk_stop(&ctrl->bus);
+ if (ret < 0 && ret != -ENODATA) {
+ dev_err(dev, "bus clock stop failed %d", ret);
+ return ret;
+ }
+ }
+
+ clk_disable_unprepare(ctrl->hclk);
+
+ usleep_range(300, 305);
+
+ if (ctrl->wake_irq > 0) {
+ if (irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq)))
+ enable_irq(ctrl->wake_irq);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops swrm_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(swrm_runtime_suspend, swrm_runtime_resume, NULL)
+};
+
static const struct of_device_id qcom_swrm_of_match[] = {
{ .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data },
{ .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data },
@@ -1359,6 +1560,7 @@ static struct platform_driver qcom_swrm_driver = {
.driver = {
.name = "qcom-soundwire",
.of_match_table = qcom_swrm_of_match,
+ .pm = &swrm_dev_pm_ops,
}
};
module_platform_driver(qcom_swrm_driver);
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index 980f26d49b66..f273459b2023 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -865,117 +865,177 @@ msg_unlock:
return ret;
}
-/**
- * sdw_release_stream() - Free the assigned stream runtime
- *
- * @stream: SoundWire stream runtime
- *
- * sdw_release_stream should be called only once per stream
- */
-void sdw_release_stream(struct sdw_stream_runtime *stream)
+static struct sdw_port_runtime *sdw_port_alloc(struct list_head *port_list)
{
- kfree(stream);
+ struct sdw_port_runtime *p_rt;
+
+ p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL);
+ if (!p_rt)
+ return NULL;
+
+ list_add_tail(&p_rt->port_node, port_list);
+
+ return p_rt;
}
-EXPORT_SYMBOL(sdw_release_stream);
-/**
- * sdw_alloc_stream() - Allocate and return stream runtime
- *
- * @stream_name: SoundWire stream name
- *
- * Allocates a SoundWire stream runtime instance.
- * sdw_alloc_stream should be called only once per stream. Typically
- * invoked from ALSA/ASoC machine/platform driver.
- */
-struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name)
+static int sdw_port_config(struct sdw_port_runtime *p_rt,
+ struct sdw_port_config *port_config,
+ int port_index)
{
- struct sdw_stream_runtime *stream;
+ p_rt->ch_mask = port_config[port_index].ch_mask;
+ p_rt->num = port_config[port_index].num;
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return NULL;
+ /*
+ * TODO: Check port capabilities for requested configuration
+ */
- stream->name = stream_name;
- INIT_LIST_HEAD(&stream->master_list);
- stream->state = SDW_STREAM_ALLOCATED;
- stream->m_rt_count = 0;
+ return 0;
+}
- return stream;
+static void sdw_port_free(struct sdw_port_runtime *p_rt)
+{
+ list_del(&p_rt->port_node);
+ kfree(p_rt);
}
-EXPORT_SYMBOL(sdw_alloc_stream);
-static struct sdw_master_runtime
-*sdw_find_master_rt(struct sdw_bus *bus,
- struct sdw_stream_runtime *stream)
+static bool sdw_slave_port_allocated(struct sdw_slave_runtime *s_rt)
+{
+ return !list_empty(&s_rt->port_list);
+}
+
+static void sdw_slave_port_free(struct sdw_slave *slave,
+ struct sdw_stream_runtime *stream)
{
+ struct sdw_port_runtime *p_rt, *_p_rt;
struct sdw_master_runtime *m_rt;
+ struct sdw_slave_runtime *s_rt;
- /* Retrieve Bus handle if already available */
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
- if (m_rt->bus == bus)
- return m_rt;
+ list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+ if (s_rt->slave != slave)
+ continue;
+
+ list_for_each_entry_safe(p_rt, _p_rt,
+ &s_rt->port_list, port_node) {
+ sdw_port_free(p_rt);
+ }
+ }
}
+}
- return NULL;
+static int sdw_slave_port_alloc(struct sdw_slave *slave,
+ struct sdw_slave_runtime *s_rt,
+ unsigned int num_config)
+{
+ struct sdw_port_runtime *p_rt;
+ int i;
+
+ /* Iterate for number of ports to perform initialization */
+ for (i = 0; i < num_config; i++) {
+ p_rt = sdw_port_alloc(&s_rt->port_list);
+ if (!p_rt)
+ return -ENOMEM;
+ }
+
+ return 0;
}
-/**
- * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle
- *
- * @bus: SDW bus instance
- * @stream_config: Stream configuration
- * @stream: Stream runtime handle.
- *
- * This function is to be called with bus_lock held.
- */
-static struct sdw_master_runtime
-*sdw_alloc_master_rt(struct sdw_bus *bus,
- struct sdw_stream_config *stream_config,
- struct sdw_stream_runtime *stream)
+static int sdw_slave_port_is_valid_range(struct device *dev, int num)
{
- struct sdw_master_runtime *m_rt;
+ if (!SDW_VALID_PORT_RANGE(num)) {
+ dev_err(dev, "SoundWire: Invalid port number :%d\n", num);
+ return -EINVAL;
+ }
- /*
- * check if Master is already allocated (as a result of Slave adding
- * it first), if so skip allocation and go to configure
- */
- m_rt = sdw_find_master_rt(bus, stream);
- if (m_rt)
- goto stream_config;
+ return 0;
+}
- m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
- if (!m_rt)
- return NULL;
+static int sdw_slave_port_config(struct sdw_slave *slave,
+ struct sdw_slave_runtime *s_rt,
+ struct sdw_port_config *port_config)
+{
+ struct sdw_port_runtime *p_rt;
+ int ret;
+ int i;
- /* Initialization of Master runtime handle */
- INIT_LIST_HEAD(&m_rt->port_list);
- INIT_LIST_HEAD(&m_rt->slave_rt_list);
- list_add_tail(&m_rt->stream_node, &stream->master_list);
+ i = 0;
+ list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+ /*
+ * TODO: Check valid port range as defined by DisCo/
+ * slave
+ */
+ ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num);
+ if (ret < 0)
+ return ret;
- list_add_tail(&m_rt->bus_node, &bus->m_rt_list);
+ ret = sdw_port_config(p_rt, port_config, i);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
-stream_config:
- m_rt->ch_count = stream_config->ch_count;
- m_rt->bus = bus;
- m_rt->stream = stream;
- m_rt->direction = stream_config->direction;
+ return 0;
+}
- return m_rt;
+static bool sdw_master_port_allocated(struct sdw_master_runtime *m_rt)
+{
+ return !list_empty(&m_rt->port_list);
+}
+
+static void sdw_master_port_free(struct sdw_master_runtime *m_rt)
+{
+ struct sdw_port_runtime *p_rt, *_p_rt;
+
+ list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) {
+ sdw_port_free(p_rt);
+ }
+}
+
+static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt,
+ unsigned int num_ports)
+{
+ struct sdw_port_runtime *p_rt;
+ int i;
+
+ /* Iterate for number of ports to perform initialization */
+ for (i = 0; i < num_ports; i++) {
+ p_rt = sdw_port_alloc(&m_rt->port_list);
+ if (!p_rt)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int sdw_master_port_config(struct sdw_master_runtime *m_rt,
+ struct sdw_port_config *port_config)
+{
+ struct sdw_port_runtime *p_rt;
+ int ret;
+ int i;
+
+ i = 0;
+ list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+ ret = sdw_port_config(p_rt, port_config, i);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+
+ return 0;
}
/**
- * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle.
+ * sdw_slave_rt_alloc() - Allocate a Slave runtime handle.
*
* @slave: Slave handle
- * @stream_config: Stream configuration
- * @stream: Stream runtime handle
+ * @m_rt: Master runtime handle
*
* This function is to be called with bus_lock held.
*/
static struct sdw_slave_runtime
-*sdw_alloc_slave_rt(struct sdw_slave *slave,
- struct sdw_stream_config *stream_config,
- struct sdw_stream_runtime *stream)
+*sdw_slave_rt_alloc(struct sdw_slave *slave,
+ struct sdw_master_runtime *m_rt)
{
struct sdw_slave_runtime *s_rt;
@@ -984,154 +1044,156 @@ static struct sdw_slave_runtime
return NULL;
INIT_LIST_HEAD(&s_rt->port_list);
- s_rt->ch_count = stream_config->ch_count;
- s_rt->direction = stream_config->direction;
s_rt->slave = slave;
+ list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
+
return s_rt;
}
-static void sdw_master_port_release(struct sdw_bus *bus,
- struct sdw_master_runtime *m_rt)
+/**
+ * sdw_slave_rt_config() - Configure a Slave runtime handle.
+ *
+ * @s_rt: Slave runtime handle
+ * @stream_config: Stream configuration
+ *
+ * This function is to be called with bus_lock held.
+ */
+static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt,
+ struct sdw_stream_config *stream_config)
{
- struct sdw_port_runtime *p_rt, *_p_rt;
+ s_rt->ch_count = stream_config->ch_count;
+ s_rt->direction = stream_config->direction;
- list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) {
- list_del(&p_rt->port_node);
- kfree(p_rt);
- }
+ return 0;
}
-static void sdw_slave_port_release(struct sdw_bus *bus,
- struct sdw_slave *slave,
- struct sdw_stream_runtime *stream)
+static struct sdw_slave_runtime *sdw_slave_rt_find(struct sdw_slave *slave,
+ struct sdw_stream_runtime *stream)
{
- struct sdw_port_runtime *p_rt, *_p_rt;
+ struct sdw_slave_runtime *s_rt, *_s_rt;
struct sdw_master_runtime *m_rt;
- struct sdw_slave_runtime *s_rt;
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
- list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
- if (s_rt->slave != slave)
- continue;
-
- list_for_each_entry_safe(p_rt, _p_rt,
- &s_rt->port_list, port_node) {
- list_del(&p_rt->port_node);
- kfree(p_rt);
- }
+ /* Retrieve Slave runtime handle */
+ list_for_each_entry_safe(s_rt, _s_rt,
+ &m_rt->slave_rt_list, m_rt_node) {
+ if (s_rt->slave == slave)
+ return s_rt;
}
}
+ return NULL;
}
/**
- * sdw_release_slave_stream() - Free Slave(s) runtime handle
+ * sdw_slave_rt_free() - Free Slave(s) runtime handle
*
* @slave: Slave handle.
* @stream: Stream runtime handle.
*
* This function is to be called with bus_lock held.
*/
-static void sdw_release_slave_stream(struct sdw_slave *slave,
- struct sdw_stream_runtime *stream)
+static void sdw_slave_rt_free(struct sdw_slave *slave,
+ struct sdw_stream_runtime *stream)
+{
+ struct sdw_slave_runtime *s_rt;
+
+ s_rt = sdw_slave_rt_find(slave, stream);
+ if (s_rt) {
+ list_del(&s_rt->m_rt_node);
+ kfree(s_rt);
+ }
+}
+
+static struct sdw_master_runtime
+*sdw_master_rt_find(struct sdw_bus *bus,
+ struct sdw_stream_runtime *stream)
{
- struct sdw_slave_runtime *s_rt, *_s_rt;
struct sdw_master_runtime *m_rt;
+ /* Retrieve Bus handle if already available */
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
- /* Retrieve Slave runtime handle */
- list_for_each_entry_safe(s_rt, _s_rt,
- &m_rt->slave_rt_list, m_rt_node) {
- if (s_rt->slave == slave) {
- list_del(&s_rt->m_rt_node);
- kfree(s_rt);
- return;
- }
- }
+ if (m_rt->bus == bus)
+ return m_rt;
}
+
+ return NULL;
}
/**
- * sdw_release_master_stream() - Free Master runtime handle
+ * sdw_master_rt_alloc() - Allocates a Master runtime handle
*
- * @m_rt: Master runtime node
+ * @bus: SDW bus instance
* @stream: Stream runtime handle.
*
- * This function is to be called with bus_lock held
- * It frees the Master runtime handle and associated Slave(s) runtime
- * handle. If this is called first then sdw_release_slave_stream() will have
- * no effect as Slave(s) runtime handle would already be freed up.
+ * This function is to be called with bus_lock held.
*/
-static void sdw_release_master_stream(struct sdw_master_runtime *m_rt,
- struct sdw_stream_runtime *stream)
+static struct sdw_master_runtime
+*sdw_master_rt_alloc(struct sdw_bus *bus,
+ struct sdw_stream_runtime *stream)
{
- struct sdw_slave_runtime *s_rt, *_s_rt;
+ struct sdw_master_runtime *m_rt;
- list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) {
- sdw_slave_port_release(s_rt->slave->bus, s_rt->slave, stream);
- sdw_release_slave_stream(s_rt->slave, stream);
- }
+ m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
+ if (!m_rt)
+ return NULL;
- list_del(&m_rt->stream_node);
- list_del(&m_rt->bus_node);
- kfree(m_rt);
+ /* Initialization of Master runtime handle */
+ INIT_LIST_HEAD(&m_rt->port_list);
+ INIT_LIST_HEAD(&m_rt->slave_rt_list);
+ list_add_tail(&m_rt->stream_node, &stream->master_list);
+
+ list_add_tail(&m_rt->bus_node, &bus->m_rt_list);
+
+ m_rt->bus = bus;
+ m_rt->stream = stream;
+
+ return m_rt;
}
/**
- * sdw_stream_remove_master() - Remove master from sdw_stream
+ * sdw_master_rt_config() - Configure Master runtime handle
*
- * @bus: SDW Bus instance
- * @stream: SoundWire stream
+ * @m_rt: Master runtime handle
+ * @stream_config: Stream configuration
*
- * This removes and frees port_rt and master_rt from a stream
+ * This function is to be called with bus_lock held.
*/
-int sdw_stream_remove_master(struct sdw_bus *bus,
- struct sdw_stream_runtime *stream)
-{
- struct sdw_master_runtime *m_rt, *_m_rt;
-
- mutex_lock(&bus->bus_lock);
-
- list_for_each_entry_safe(m_rt, _m_rt,
- &stream->master_list, stream_node) {
- if (m_rt->bus != bus)
- continue;
-
- sdw_master_port_release(bus, m_rt);
- sdw_release_master_stream(m_rt, stream);
- stream->m_rt_count--;
- }
-
- if (list_empty(&stream->master_list))
- stream->state = SDW_STREAM_RELEASED;
- mutex_unlock(&bus->bus_lock);
+static int sdw_master_rt_config(struct sdw_master_runtime *m_rt,
+ struct sdw_stream_config *stream_config)
+{
+ m_rt->ch_count = stream_config->ch_count;
+ m_rt->direction = stream_config->direction;
return 0;
}
-EXPORT_SYMBOL(sdw_stream_remove_master);
/**
- * sdw_stream_remove_slave() - Remove slave from sdw_stream
+ * sdw_master_rt_free() - Free Master runtime handle
*
- * @slave: SDW Slave instance
- * @stream: SoundWire stream
+ * @m_rt: Master runtime node
+ * @stream: Stream runtime handle.
*
- * This removes and frees port_rt and slave_rt from a stream
+ * This function is to be called with bus_lock held
+ * It frees the Master runtime handle and associated Slave(s) runtime
+ * handle. If this is called first then sdw_slave_rt_free() will have
+ * no effect as Slave(s) runtime handle would already be freed up.
*/
-int sdw_stream_remove_slave(struct sdw_slave *slave,
- struct sdw_stream_runtime *stream)
+static void sdw_master_rt_free(struct sdw_master_runtime *m_rt,
+ struct sdw_stream_runtime *stream)
{
- mutex_lock(&slave->bus->bus_lock);
-
- sdw_slave_port_release(slave->bus, slave, stream);
- sdw_release_slave_stream(slave, stream);
+ struct sdw_slave_runtime *s_rt, *_s_rt;
- mutex_unlock(&slave->bus->bus_lock);
+ list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) {
+ sdw_slave_port_free(s_rt->slave, stream);
+ sdw_slave_rt_free(s_rt->slave, stream);
+ }
- return 0;
+ list_del(&m_rt->stream_node);
+ list_del(&m_rt->bus_node);
+ kfree(m_rt);
}
-EXPORT_SYMBOL(sdw_stream_remove_slave);
/**
* sdw_config_stream() - Configure the allocated stream
@@ -1179,242 +1241,6 @@ static int sdw_config_stream(struct device *dev,
return 0;
}
-static int sdw_is_valid_port_range(struct device *dev,
- struct sdw_port_runtime *p_rt)
-{
- if (!SDW_VALID_PORT_RANGE(p_rt->num)) {
- dev_err(dev,
- "SoundWire: Invalid port number :%d\n", p_rt->num);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static struct sdw_port_runtime
-*sdw_port_alloc(struct device *dev,
- struct sdw_port_config *port_config,
- int port_index)
-{
- struct sdw_port_runtime *p_rt;
-
- p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL);
- if (!p_rt)
- return NULL;
-
- p_rt->ch_mask = port_config[port_index].ch_mask;
- p_rt->num = port_config[port_index].num;
-
- return p_rt;
-}
-
-static int sdw_master_port_config(struct sdw_bus *bus,
- struct sdw_master_runtime *m_rt,
- struct sdw_port_config *port_config,
- unsigned int num_ports)
-{
- struct sdw_port_runtime *p_rt;
- int i;
-
- /* Iterate for number of ports to perform initialization */
- for (i = 0; i < num_ports; i++) {
- p_rt = sdw_port_alloc(bus->dev, port_config, i);
- if (!p_rt)
- return -ENOMEM;
-
- /*
- * TODO: Check port capabilities for requested
- * configuration (audio mode support)
- */
-
- list_add_tail(&p_rt->port_node, &m_rt->port_list);
- }
-
- return 0;
-}
-
-static int sdw_slave_port_config(struct sdw_slave *slave,
- struct sdw_slave_runtime *s_rt,
- struct sdw_port_config *port_config,
- unsigned int num_config)
-{
- struct sdw_port_runtime *p_rt;
- int i, ret;
-
- /* Iterate for number of ports to perform initialization */
- for (i = 0; i < num_config; i++) {
- p_rt = sdw_port_alloc(&slave->dev, port_config, i);
- if (!p_rt)
- return -ENOMEM;
-
- /*
- * TODO: Check valid port range as defined by DisCo/
- * slave
- */
- ret = sdw_is_valid_port_range(&slave->dev, p_rt);
- if (ret < 0) {
- kfree(p_rt);
- return ret;
- }
-
- /*
- * TODO: Check port capabilities for requested
- * configuration (audio mode support)
- */
-
- list_add_tail(&p_rt->port_node, &s_rt->port_list);
- }
-
- return 0;
-}
-
-/**
- * sdw_stream_add_master() - Allocate and add master runtime to a stream
- *
- * @bus: SDW Bus instance
- * @stream_config: Stream configuration for audio stream
- * @port_config: Port configuration for audio stream
- * @num_ports: Number of ports
- * @stream: SoundWire stream
- */
-int sdw_stream_add_master(struct sdw_bus *bus,
- struct sdw_stream_config *stream_config,
- struct sdw_port_config *port_config,
- unsigned int num_ports,
- struct sdw_stream_runtime *stream)
-{
- struct sdw_master_runtime *m_rt;
- int ret;
-
- mutex_lock(&bus->bus_lock);
-
- /*
- * For multi link streams, add the second master only if
- * the bus supports it.
- * Check if bus->multi_link is set
- */
- if (!bus->multi_link && stream->m_rt_count > 0) {
- dev_err(bus->dev,
- "Multilink not supported, link %d\n", bus->link_id);
- ret = -EINVAL;
- goto unlock;
- }
-
- m_rt = sdw_alloc_master_rt(bus, stream_config, stream);
- if (!m_rt) {
- dev_err(bus->dev,
- "Master runtime config failed for stream:%s\n",
- stream->name);
- ret = -ENOMEM;
- goto unlock;
- }
-
- ret = sdw_config_stream(bus->dev, stream, stream_config, false);
- if (ret)
- goto stream_error;
-
- ret = sdw_master_port_config(bus, m_rt, port_config, num_ports);
- if (ret)
- goto stream_error;
-
- stream->m_rt_count++;
-
- goto unlock;
-
-stream_error:
- sdw_release_master_stream(m_rt, stream);
-unlock:
- mutex_unlock(&bus->bus_lock);
- return ret;
-}
-EXPORT_SYMBOL(sdw_stream_add_master);
-
-/**
- * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream
- *
- * @slave: SDW Slave instance
- * @stream_config: Stream configuration for audio stream
- * @stream: SoundWire stream
- * @port_config: Port configuration for audio stream
- * @num_ports: Number of ports
- *
- * It is expected that Slave is added before adding Master
- * to the Stream.
- *
- */
-int sdw_stream_add_slave(struct sdw_slave *slave,
- struct sdw_stream_config *stream_config,
- struct sdw_port_config *port_config,
- unsigned int num_ports,
- struct sdw_stream_runtime *stream)
-{
- struct sdw_slave_runtime *s_rt;
- struct sdw_master_runtime *m_rt;
- int ret;
-
- mutex_lock(&slave->bus->bus_lock);
-
- /*
- * If this API is invoked by Slave first then m_rt is not valid.
- * So, allocate m_rt and add Slave to it.
- */
- m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream);
- if (!m_rt) {
- dev_err(&slave->dev,
- "alloc master runtime failed for stream:%s\n",
- stream->name);
- ret = -ENOMEM;
- goto error;
- }
-
- s_rt = sdw_alloc_slave_rt(slave, stream_config, stream);
- if (!s_rt) {
- dev_err(&slave->dev,
- "Slave runtime config failed for stream:%s\n",
- stream->name);
- ret = -ENOMEM;
- goto stream_error;
- }
-
- ret = sdw_config_stream(&slave->dev, stream, stream_config, true);
- if (ret) {
- /*
- * sdw_release_master_stream will release s_rt in slave_rt_list in
- * stream_error case, but s_rt is only added to slave_rt_list
- * when sdw_config_stream is successful, so free s_rt explicitly
- * when sdw_config_stream is failed.
- */
- kfree(s_rt);
- goto stream_error;
- }
-
- list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
-
- ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports);
- if (ret)
- goto stream_error;
-
- /*
- * Change stream state to CONFIGURED on first Slave add.
- * Bus is not aware of number of Slave(s) in a stream at this
- * point so cannot depend on all Slave(s) to be added in order to
- * change stream state to CONFIGURED.
- */
- stream->state = SDW_STREAM_CONFIGURED;
- goto error;
-
-stream_error:
- /*
- * we hit error so cleanup the stream, release all Slave(s) and
- * Master runtime
- */
- sdw_release_master_stream(m_rt, stream);
-error:
- mutex_unlock(&slave->bus->bus_lock);
- return ret;
-}
-EXPORT_SYMBOL(sdw_stream_add_slave);
-
/**
* sdw_get_slave_dpn_prop() - Get Slave port capabilities
*
@@ -1679,6 +1505,11 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
+ if (stream->state == SDW_STREAM_ENABLED) {
+ ret = 0;
+ goto state_err;
+ }
+
if (stream->state != SDW_STREAM_PREPARED &&
stream->state != SDW_STREAM_DISABLED) {
pr_err("%s: %s: inconsistent state state %d\n",
@@ -1762,6 +1593,11 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
+ if (stream->state == SDW_STREAM_DISABLED) {
+ ret = 0;
+ goto state_err;
+ }
+
if (stream->state != SDW_STREAM_ENABLED) {
pr_err("%s: %s: inconsistent state state %d\n",
__func__, stream->name, stream->state);
@@ -1837,6 +1673,11 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream);
+ if (stream->state == SDW_STREAM_DEPREPARED) {
+ ret = 0;
+ goto state_err;
+ }
+
if (stream->state != SDW_STREAM_PREPARED &&
stream->state != SDW_STREAM_DISABLED) {
pr_err("%s: %s: inconsistent state state %d\n",
@@ -1874,6 +1715,32 @@ static int set_stream(struct snd_pcm_substream *substream,
}
/**
+ * sdw_alloc_stream() - Allocate and return stream runtime
+ *
+ * @stream_name: SoundWire stream name
+ *
+ * Allocates a SoundWire stream runtime instance.
+ * sdw_alloc_stream should be called only once per stream. Typically
+ * invoked from ALSA/ASoC machine/platform driver.
+ */
+struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name)
+{
+ struct sdw_stream_runtime *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return NULL;
+
+ stream->name = stream_name;
+ INIT_LIST_HEAD(&stream->master_list);
+ stream->state = SDW_STREAM_ALLOCATED;
+ stream->m_rt_count = 0;
+
+ return stream;
+}
+EXPORT_SYMBOL(sdw_alloc_stream);
+
+/**
* sdw_startup_stream() - Startup SoundWire stream
*
* @sdw_substream: Soundwire stream
@@ -1949,3 +1816,270 @@ void sdw_shutdown_stream(void *sdw_substream)
set_stream(substream, NULL);
}
EXPORT_SYMBOL(sdw_shutdown_stream);
+
+/**
+ * sdw_release_stream() - Free the assigned stream runtime
+ *
+ * @stream: SoundWire stream runtime
+ *
+ * sdw_release_stream should be called only once per stream
+ */
+void sdw_release_stream(struct sdw_stream_runtime *stream)
+{
+ kfree(stream);
+}
+EXPORT_SYMBOL(sdw_release_stream);
+
+/**
+ * sdw_stream_add_master() - Allocate and add master runtime to a stream
+ *
+ * @bus: SDW Bus instance
+ * @stream_config: Stream configuration for audio stream
+ * @port_config: Port configuration for audio stream
+ * @num_ports: Number of ports
+ * @stream: SoundWire stream
+ */
+int sdw_stream_add_master(struct sdw_bus *bus,
+ struct sdw_stream_config *stream_config,
+ struct sdw_port_config *port_config,
+ unsigned int num_ports,
+ struct sdw_stream_runtime *stream)
+{
+ struct sdw_master_runtime *m_rt;
+ bool alloc_master_rt = true;
+ int ret;
+
+ mutex_lock(&bus->bus_lock);
+
+ /*
+ * For multi link streams, add the second master only if
+ * the bus supports it.
+ * Check if bus->multi_link is set
+ */
+ if (!bus->multi_link && stream->m_rt_count > 0) {
+ dev_err(bus->dev,
+ "Multilink not supported, link %d\n", bus->link_id);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /*
+ * check if Master is already allocated (e.g. as a result of Slave adding
+ * it first), if so skip allocation and go to configuration
+ */
+ m_rt = sdw_master_rt_find(bus, stream);
+ if (m_rt) {
+ alloc_master_rt = false;
+ goto skip_alloc_master_rt;
+ }
+
+ m_rt = sdw_master_rt_alloc(bus, stream);
+ if (!m_rt) {
+ dev_err(bus->dev, "Master runtime alloc failed for stream:%s\n", stream->name);
+ ret = -ENOMEM;
+ goto unlock;
+ }
+skip_alloc_master_rt:
+
+ if (sdw_master_port_allocated(m_rt))
+ goto skip_alloc_master_port;
+
+ ret = sdw_master_port_alloc(m_rt, num_ports);
+ if (ret)
+ goto alloc_error;
+
+ stream->m_rt_count++;
+
+skip_alloc_master_port:
+
+ ret = sdw_master_rt_config(m_rt, stream_config);
+ if (ret < 0)
+ goto unlock;
+
+ ret = sdw_config_stream(bus->dev, stream, stream_config, false);
+ if (ret)
+ goto unlock;
+
+ ret = sdw_master_port_config(m_rt, port_config);
+
+ goto unlock;
+
+alloc_error:
+ /*
+ * we only cleanup what was allocated in this routine
+ */
+ if (alloc_master_rt)
+ sdw_master_rt_free(m_rt, stream);
+unlock:
+ mutex_unlock(&bus->bus_lock);
+ return ret;
+}
+EXPORT_SYMBOL(sdw_stream_add_master);
+
+/**
+ * sdw_stream_remove_master() - Remove master from sdw_stream
+ *
+ * @bus: SDW Bus instance
+ * @stream: SoundWire stream
+ *
+ * This removes and frees port_rt and master_rt from a stream
+ */
+int sdw_stream_remove_master(struct sdw_bus *bus,
+ struct sdw_stream_runtime *stream)
+{
+ struct sdw_master_runtime *m_rt, *_m_rt;
+
+ mutex_lock(&bus->bus_lock);
+
+ list_for_each_entry_safe(m_rt, _m_rt,
+ &stream->master_list, stream_node) {
+ if (m_rt->bus != bus)
+ continue;
+
+ sdw_master_port_free(m_rt);
+ sdw_master_rt_free(m_rt, stream);
+ stream->m_rt_count--;
+ }
+
+ if (list_empty(&stream->master_list))
+ stream->state = SDW_STREAM_RELEASED;
+
+ mutex_unlock(&bus->bus_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(sdw_stream_remove_master);
+
+/**
+ * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream
+ *
+ * @slave: SDW Slave instance
+ * @stream_config: Stream configuration for audio stream
+ * @stream: SoundWire stream
+ * @port_config: Port configuration for audio stream
+ * @num_ports: Number of ports
+ *
+ * It is expected that Slave is added before adding Master
+ * to the Stream.
+ *
+ */
+int sdw_stream_add_slave(struct sdw_slave *slave,
+ struct sdw_stream_config *stream_config,
+ struct sdw_port_config *port_config,
+ unsigned int num_ports,
+ struct sdw_stream_runtime *stream)
+{
+ struct sdw_slave_runtime *s_rt;
+ struct sdw_master_runtime *m_rt;
+ bool alloc_master_rt = true;
+ bool alloc_slave_rt = true;
+
+ int ret;
+
+ mutex_lock(&slave->bus->bus_lock);
+
+ /*
+ * check if Master is already allocated, if so skip allocation
+ * and go to configuration
+ */
+ m_rt = sdw_master_rt_find(slave->bus, stream);
+ if (m_rt) {
+ alloc_master_rt = false;
+ goto skip_alloc_master_rt;
+ }
+
+ /*
+ * If this API is invoked by Slave first then m_rt is not valid.
+ * So, allocate m_rt and add Slave to it.
+ */
+ m_rt = sdw_master_rt_alloc(slave->bus, stream);
+ if (!m_rt) {
+ dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name);
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+skip_alloc_master_rt:
+ s_rt = sdw_slave_rt_find(slave, stream);
+ if (s_rt)
+ goto skip_alloc_slave_rt;
+
+ s_rt = sdw_slave_rt_alloc(slave, m_rt);
+ if (!s_rt) {
+ dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name);
+ alloc_slave_rt = false;
+ ret = -ENOMEM;
+ goto alloc_error;
+ }
+
+skip_alloc_slave_rt:
+ if (sdw_slave_port_allocated(s_rt))
+ goto skip_port_alloc;
+
+ ret = sdw_slave_port_alloc(slave, s_rt, num_ports);
+ if (ret)
+ goto alloc_error;
+
+skip_port_alloc:
+ ret = sdw_master_rt_config(m_rt, stream_config);
+ if (ret)
+ goto unlock;
+
+ ret = sdw_slave_rt_config(s_rt, stream_config);
+ if (ret)
+ goto unlock;
+
+ ret = sdw_config_stream(&slave->dev, stream, stream_config, true);
+ if (ret)
+ goto unlock;
+
+ ret = sdw_slave_port_config(slave, s_rt, port_config);
+ if (ret)
+ goto unlock;
+
+ /*
+ * Change stream state to CONFIGURED on first Slave add.
+ * Bus is not aware of number of Slave(s) in a stream at this
+ * point so cannot depend on all Slave(s) to be added in order to
+ * change stream state to CONFIGURED.
+ */
+ stream->state = SDW_STREAM_CONFIGURED;
+ goto unlock;
+
+alloc_error:
+ /*
+ * we only cleanup what was allocated in this routine. The 'else if'
+ * is intentional, the 'master_rt_free' will call sdw_slave_rt_free()
+ * internally.
+ */
+ if (alloc_master_rt)
+ sdw_master_rt_free(m_rt, stream);
+ else if (alloc_slave_rt)
+ sdw_slave_rt_free(slave, stream);
+unlock:
+ mutex_unlock(&slave->bus->bus_lock);
+ return ret;
+}
+EXPORT_SYMBOL(sdw_stream_add_slave);
+
+/**
+ * sdw_stream_remove_slave() - Remove slave from sdw_stream
+ *
+ * @slave: SDW Slave instance
+ * @stream: SoundWire stream
+ *
+ * This removes and frees port_rt and slave_rt from a stream
+ */
+int sdw_stream_remove_slave(struct sdw_slave *slave,
+ struct sdw_stream_runtime *stream)
+{
+ mutex_lock(&slave->bus->bus_lock);
+
+ sdw_slave_port_free(slave, stream);
+ sdw_slave_rt_free(slave, stream);
+
+ mutex_unlock(&slave->bus->bus_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(sdw_stream_remove_slave);
diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c
index 1d3026dae827..62d5397ff1f9 100644
--- a/drivers/staging/iio/accel/adis16203.c
+++ b/drivers/staging/iio/accel/adis16203.c
@@ -312,3 +312,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:adis16203");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c
index 2a8aa83b8d9e..bca857eef92e 100644
--- a/drivers/staging/iio/accel/adis16240.c
+++ b/drivers/staging/iio/accel/adis16240.c
@@ -440,3 +440,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:adis16240");
+MODULE_IMPORT_NS(IIO_ADISLIB);
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index b25f41053fac..2f0d6cf048d2 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -15,15 +15,4 @@ config AD7816
To compile this driver as a module, choose M here: the
module will be called ad7816.
-config AD7280
- tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
- depends on SPI
- select CRC8
- help
- Say yes here to build support for Analog Devices AD7280A
- Lithium Ion Battery Monitoring System.
-
- To compile this driver as a module, choose M here: the
- module will be called ad7280a
-
endmenu
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 6436a62b6278..1e2a94c4db84 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -4,4 +4,3 @@
#
obj-$(CONFIG_AD7816) += ad7816.o
-obj-$(CONFIG_AD7280) += ad7280a.o
diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c
deleted file mode 100644
index fef0055b8990..000000000000
--- a/drivers/staging/iio/adc/ad7280a.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * AD7280A Lithium Ion Battery Monitoring System
- *
- * Copyright 2011 Analog Devices Inc.
- */
-
-#include <linux/crc8.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/sysfs.h>
-#include <linux/spi/spi.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
-#include <linux/iio/events.h>
-
-#include "ad7280a.h"
-
-/* Registers */
-#define AD7280A_CELL_VOLTAGE_1 0x0 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_2 0x1 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_3 0x2 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_4 0x3 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_5 0x4 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_6 0x5 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_1 0x6 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_2 0x7 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_3 0x8 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_4 0x9 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_5 0xA /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_6 0xB /* D11 to D0, Read only */
-#define AD7280A_SELF_TEST 0xC /* D11 to D0, Read only */
-#define AD7280A_CONTROL_HB 0xD /* D15 to D8, Read/write */
-#define AD7280A_CONTROL_LB 0xE /* D7 to D0, Read/write */
-#define AD7280A_CELL_OVERVOLTAGE 0xF /* D7 to D0, Read/write */
-#define AD7280A_CELL_UNDERVOLTAGE 0x10 /* D7 to D0, Read/write */
-#define AD7280A_AUX_ADC_OVERVOLTAGE 0x11 /* D7 to D0, Read/write */
-#define AD7280A_AUX_ADC_UNDERVOLTAGE 0x12 /* D7 to D0, Read/write */
-#define AD7280A_ALERT 0x13 /* D7 to D0, Read/write */
-#define AD7280A_CELL_BALANCE 0x14 /* D7 to D0, Read/write */
-#define AD7280A_CB1_TIMER 0x15 /* D7 to D0, Read/write */
-#define AD7280A_CB2_TIMER 0x16 /* D7 to D0, Read/write */
-#define AD7280A_CB3_TIMER 0x17 /* D7 to D0, Read/write */
-#define AD7280A_CB4_TIMER 0x18 /* D7 to D0, Read/write */
-#define AD7280A_CB5_TIMER 0x19 /* D7 to D0, Read/write */
-#define AD7280A_CB6_TIMER 0x1A /* D7 to D0, Read/write */
-#define AD7280A_PD_TIMER 0x1B /* D7 to D0, Read/write */
-#define AD7280A_READ 0x1C /* D7 to D0, Read/write */
-#define AD7280A_CNVST_CONTROL 0x1D /* D7 to D0, Read/write */
-
-/* Bits and Masks */
-#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0
-#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_4 BIT(6)
-#define AD7280A_CTRL_HB_CONV_INPUT_6CELL BIT(7)
-#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST (BIT(7) | BIT(6))
-#define AD7280A_CTRL_HB_CONV_RES_READ_ALL 0
-#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL_AUX1_3_4 BIT(4)
-#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL BIT(5)
-#define AD7280A_CTRL_HB_CONV_RES_READ_NO (BIT(5) | BIT(4))
-#define AD7280A_CTRL_HB_CONV_START_CNVST 0
-#define AD7280A_CTRL_HB_CONV_START_CS BIT(3)
-#define AD7280A_CTRL_HB_CONV_AVG_DIS 0
-#define AD7280A_CTRL_HB_CONV_AVG_2 BIT(1)
-#define AD7280A_CTRL_HB_CONV_AVG_4 BIT(2)
-#define AD7280A_CTRL_HB_CONV_AVG_8 (BIT(2) | BIT(1))
-#define AD7280A_CTRL_HB_CONV_AVG(x) ((x) << 1)
-#define AD7280A_CTRL_HB_PWRDN_SW BIT(0)
-
-#define AD7280A_CTRL_LB_SWRST BIT(7)
-#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0
-#define AD7280A_CTRL_LB_ACQ_TIME_800ns BIT(5)
-#define AD7280A_CTRL_LB_ACQ_TIME_1200ns BIT(6)
-#define AD7280A_CTRL_LB_ACQ_TIME_1600ns (BIT(6) | BIT(5))
-#define AD7280A_CTRL_LB_ACQ_TIME(x) ((x) << 5)
-#define AD7280A_CTRL_LB_MUST_SET BIT(4)
-#define AD7280A_CTRL_LB_THERMISTOR_EN BIT(3)
-#define AD7280A_CTRL_LB_LOCK_DEV_ADDR BIT(2)
-#define AD7280A_CTRL_LB_INC_DEV_ADDR BIT(1)
-#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN BIT(0)
-
-#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6)
-#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6))
-
-#define AD7280A_ALL_CELLS (0xAD << 16)
-
-#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */
-#define AD7280A_MAX_CHAIN 8
-#define AD7280A_CELLS_PER_DEV 6
-#define AD7280A_BITS 12
-#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \
- AD7280A_CELL_VOLTAGE_1 + 1)
-
-#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
- (c))
-#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
- (c) - AD7280A_CELLS_PER_DEV)
-
-#define AD7280A_DEVADDR_MASTER 0
-#define AD7280A_DEVADDR_ALL 0x1F
-/* 5-bit device address is sent LSB first */
-static unsigned int ad7280a_devaddr(unsigned int addr)
-{
- return ((addr & 0x1) << 4) |
- ((addr & 0x2) << 3) |
- (addr & 0x4) |
- ((addr & 0x8) >> 3) |
- ((addr & 0x10) >> 4);
-}
-
-/* During a read a valid write is mandatory.
- * So writing to the highest available address (Address 0x1F)
- * and setting the address all parts bit to 0 is recommended
- * So the TXVAL is AD7280A_DEVADDR_ALL + CRC
- */
-#define AD7280A_READ_TXVAL 0xF800030A
-
-/*
- * AD7280 CRC
- *
- * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F
- */
-#define POLYNOM 0x2F
-
-struct ad7280_state {
- struct spi_device *spi;
- struct iio_chan_spec *channels;
- struct iio_dev_attr *iio_attr;
- int slave_num;
- int scan_cnt;
- int readback_delay_us;
- unsigned char crc_tab[CRC8_TABLE_SIZE];
- unsigned char ctrl_hb;
- unsigned char ctrl_lb;
- unsigned char cell_threshhigh;
- unsigned char cell_threshlow;
- unsigned char aux_threshhigh;
- unsigned char aux_threshlow;
- unsigned char cb_mask[AD7280A_MAX_CHAIN];
- struct mutex lock; /* protect sensor state */
-
- __be32 buf[2] ____cacheline_aligned;
-};
-
-static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val)
-{
- unsigned char crc;
-
- crc = crc_tab[val >> 16 & 0xFF];
- crc = crc_tab[crc ^ (val >> 8 & 0xFF)];
-
- return crc ^ (val & 0xFF);
-}
-
-static int ad7280_check_crc(struct ad7280_state *st, unsigned int val)
-{
- unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10);
-
- if (crc != ((val >> 2) & 0xFF))
- return -EIO;
-
- return 0;
-}
-
-/* After initiating a conversion sequence we need to wait until the
- * conversion is done. The delay is typically in the range of 15..30 us
- * however depending an the number of devices in the daisy chain and the
- * number of averages taken, conversion delays and acquisition time options
- * it may take up to 250us, in this case we better sleep instead of busy
- * wait.
- */
-
-static void ad7280_delay(struct ad7280_state *st)
-{
- if (st->readback_delay_us < 50)
- udelay(st->readback_delay_us);
- else
- usleep_range(250, 500);
-}
-
-static int __ad7280_read32(struct ad7280_state *st, unsigned int *val)
-{
- int ret;
- struct spi_transfer t = {
- .tx_buf = &st->buf[0],
- .rx_buf = &st->buf[1],
- .len = 4,
- };
-
- st->buf[0] = cpu_to_be32(AD7280A_READ_TXVAL);
-
- ret = spi_sync_transfer(st->spi, &t, 1);
- if (ret)
- return ret;
-
- *val = be32_to_cpu(st->buf[1]);
-
- return 0;
-}
-
-static int ad7280_write(struct ad7280_state *st, unsigned int devaddr,
- unsigned int addr, bool all, unsigned int val)
-{
- unsigned int reg = devaddr << 27 | addr << 21 |
- (val & 0xFF) << 13 | all << 12;
-
- reg |= ad7280_calc_crc8(st->crc_tab, reg >> 11) << 3 | 0x2;
- st->buf[0] = cpu_to_be32(reg);
-
- return spi_write(st->spi, &st->buf[0], 4);
-}
-
-static int ad7280_read(struct ad7280_state *st, unsigned int devaddr,
- unsigned int addr)
-{
- int ret;
- unsigned int tmp;
-
- /* turns off the read operation on all parts */
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_NO |
- st->ctrl_hb);
- if (ret)
- return ret;
-
- /* turns on the read operation on the addressed part */
- ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_ALL |
- st->ctrl_hb);
- if (ret)
- return ret;
-
- /* Set register address on the part to be read from */
- ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2);
- if (ret)
- return ret;
-
- ret = __ad7280_read32(st, &tmp);
- if (ret)
- return ret;
-
- if (ad7280_check_crc(st, tmp))
- return -EIO;
-
- if (((tmp >> 27) != devaddr) || (((tmp >> 21) & 0x3F) != addr))
- return -EFAULT;
-
- return (tmp >> 13) & 0xFF;
-}
-
-static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr,
- unsigned int addr)
-{
- int ret;
- unsigned int tmp;
-
- ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2);
- if (ret)
- return ret;
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_NO |
- st->ctrl_hb);
- if (ret)
- return ret;
-
- ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_ALL |
- AD7280A_CTRL_HB_CONV_START_CS |
- st->ctrl_hb);
- if (ret)
- return ret;
-
- ad7280_delay(st);
-
- ret = __ad7280_read32(st, &tmp);
- if (ret)
- return ret;
-
- if (ad7280_check_crc(st, tmp))
- return -EIO;
-
- if (((tmp >> 27) != devaddr) || (((tmp >> 23) & 0xF) != addr))
- return -EFAULT;
-
- return (tmp >> 11) & 0xFFF;
-}
-
-static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
- unsigned int *array)
-{
- int i, ret;
- unsigned int tmp, sum = 0;
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
- AD7280A_CELL_VOLTAGE_1 << 2);
- if (ret)
- return ret;
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_ALL |
- AD7280A_CTRL_HB_CONV_START_CS |
- st->ctrl_hb);
- if (ret)
- return ret;
-
- ad7280_delay(st);
-
- for (i = 0; i < cnt; i++) {
- ret = __ad7280_read32(st, &tmp);
- if (ret)
- return ret;
-
- if (ad7280_check_crc(st, tmp))
- return -EIO;
-
- if (array)
- array[i] = tmp;
- /* only sum cell voltages */
- if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6)
- sum += ((tmp >> 11) & 0xFFF);
- }
-
- return sum;
-}
-
-static void ad7280_sw_power_down(void *data)
-{
- struct ad7280_state *st = data;
-
- ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
-}
-
-static int ad7280_chain_setup(struct ad7280_state *st)
-{
- unsigned int val, n;
- int ret;
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1,
- AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN |
- AD7280A_CTRL_LB_LOCK_DEV_ADDR |
- AD7280A_CTRL_LB_MUST_SET |
- AD7280A_CTRL_LB_SWRST |
- st->ctrl_lb);
- if (ret)
- return ret;
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1,
- AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN |
- AD7280A_CTRL_LB_LOCK_DEV_ADDR |
- AD7280A_CTRL_LB_MUST_SET |
- st->ctrl_lb);
- if (ret)
- goto error_power_down;
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
- AD7280A_CONTROL_LB << 2);
- if (ret)
- goto error_power_down;
-
- for (n = 0; n <= AD7280A_MAX_CHAIN; n++) {
- ret = __ad7280_read32(st, &val);
- if (ret)
- goto error_power_down;
-
- if (val == 0)
- return n - 1;
-
- if (ad7280_check_crc(st, val)) {
- ret = -EIO;
- goto error_power_down;
- }
-
- if (n != ad7280a_devaddr(val >> 27)) {
- ret = -EIO;
- goto error_power_down;
- }
- }
- ret = -EFAULT;
-
-error_power_down:
- ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
-
- return ret;
-}
-
-static ssize_t ad7280_show_balance_sw(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct ad7280_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- return sprintf(buf, "%d\n",
- !!(st->cb_mask[this_attr->address >> 8] &
- (1 << ((this_attr->address & 0xFF) + 2))));
-}
-
-static ssize_t ad7280_store_balance_sw(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct ad7280_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- bool readin;
- int ret;
- unsigned int devaddr, ch;
-
- ret = strtobool(buf, &readin);
- if (ret)
- return ret;
-
- devaddr = this_attr->address >> 8;
- ch = this_attr->address & 0xFF;
-
- mutex_lock(&st->lock);
- if (readin)
- st->cb_mask[devaddr] |= 1 << (ch + 2);
- else
- st->cb_mask[devaddr] &= ~(1 << (ch + 2));
-
- ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE,
- 0, st->cb_mask[devaddr]);
- mutex_unlock(&st->lock);
-
- return ret ? ret : len;
-}
-
-static ssize_t ad7280_show_balance_timer(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct ad7280_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- int ret;
- unsigned int msecs;
-
- mutex_lock(&st->lock);
- ret = ad7280_read(st, this_attr->address >> 8,
- this_attr->address & 0xFF);
- mutex_unlock(&st->lock);
-
- if (ret < 0)
- return ret;
-
- msecs = (ret >> 3) * 71500;
-
- return sprintf(buf, "%u\n", msecs);
-}
-
-static ssize_t ad7280_store_balance_timer(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct ad7280_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- unsigned long val;
- int ret;
-
- ret = kstrtoul(buf, 10, &val);
- if (ret)
- return ret;
-
- val /= 71500;
-
- if (val > 31)
- return -EINVAL;
-
- mutex_lock(&st->lock);
- ret = ad7280_write(st, this_attr->address >> 8,
- this_attr->address & 0xFF,
- 0, (val & 0x1F) << 3);
- mutex_unlock(&st->lock);
-
- return ret ? ret : len;
-}
-
-static struct attribute *ad7280_attributes[AD7280A_MAX_CHAIN *
- AD7280A_CELLS_PER_DEV * 2 + 1];
-
-static const struct attribute_group ad7280_attrs_group = {
- .attrs = ad7280_attributes,
-};
-
-static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i)
-{
- chan->type = IIO_VOLTAGE;
- chan->differential = 1;
- chan->channel = i;
- chan->channel2 = chan->channel + 1;
-}
-
-static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i)
-{
- chan->type = IIO_TEMP;
- chan->channel = i;
-}
-
-static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr,
- int cnt)
-{
- chan->indexed = 1;
- chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
- chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
- chan->address = addr;
- chan->scan_index = cnt;
- chan->scan_type.sign = 'u';
- chan->scan_type.realbits = 12;
- chan->scan_type.storagebits = 32;
-}
-
-static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan,
- int cnt, int dev)
-{
- chan->type = IIO_VOLTAGE;
- chan->differential = 1;
- chan->channel = 0;
- chan->channel2 = dev * AD7280A_CELLS_PER_DEV;
- chan->address = AD7280A_ALL_CELLS;
- chan->indexed = 1;
- chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
- chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
- chan->scan_index = cnt;
- chan->scan_type.sign = 'u';
- chan->scan_type.realbits = 32;
- chan->scan_type.storagebits = 32;
-}
-
-static void ad7280_timestamp_channel_init(struct iio_chan_spec *chan, int cnt)
-{
- chan->type = IIO_TIMESTAMP;
- chan->channel = -1;
- chan->scan_index = cnt;
- chan->scan_type.sign = 's';
- chan->scan_type.realbits = 64;
- chan->scan_type.storagebits = 64;
-}
-
-static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt)
-{
- int addr, ch, i;
- struct iio_chan_spec *chan;
-
- for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++) {
- chan = &st->channels[*cnt];
-
- if (ch < AD7280A_AUX_ADC_1) {
- i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch);
- ad7280_voltage_channel_init(chan, i);
- } else {
- i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch);
- ad7280_temp_channel_init(chan, i);
- }
-
- addr = ad7280a_devaddr(dev) << 8 | ch;
- ad7280_common_fields_init(chan, addr, *cnt);
-
- (*cnt)++;
- }
-}
-
-static int ad7280_channel_init(struct ad7280_state *st)
-{
- int dev, cnt = 0;
-
- st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 2,
- sizeof(*st->channels), GFP_KERNEL);
- if (!st->channels)
- return -ENOMEM;
-
- for (dev = 0; dev <= st->slave_num; dev++)
- ad7280_init_dev_channels(st, dev, &cnt);
-
- ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev);
- cnt++;
- ad7280_timestamp_channel_init(&st->channels[cnt], cnt);
-
- return cnt + 1;
-}
-
-static int ad7280_balance_switch_attr_init(struct iio_dev_attr *attr,
- struct device *dev, int addr, int i)
-{
- attr->address = addr;
- attr->dev_attr.attr.mode = 0644;
- attr->dev_attr.show = ad7280_show_balance_sw;
- attr->dev_attr.store = ad7280_store_balance_sw;
- attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
- "in%d-in%d_balance_switch_en",
- i, i + 1);
- if (!attr->dev_attr.attr.name)
- return -ENOMEM;
-
- return 0;
-}
-
-static int ad7280_balance_timer_attr_init(struct iio_dev_attr *attr,
- struct device *dev, int addr, int i)
-{
- attr->address = addr;
- attr->dev_attr.attr.mode = 0644;
- attr->dev_attr.show = ad7280_show_balance_timer;
- attr->dev_attr.store = ad7280_store_balance_timer;
- attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
- "in%d-in%d_balance_timer",
- i, i + 1);
- if (!attr->dev_attr.attr.name)
- return -ENOMEM;
-
- return 0;
-}
-
-static int ad7280_init_dev_attrs(struct ad7280_state *st, int dev, int *cnt)
-{
- int addr, ch, i, ret;
- struct iio_dev_attr *iio_attr;
- struct device *sdev = &st->spi->dev;
-
- for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; ch++) {
- iio_attr = &st->iio_attr[*cnt];
- addr = ad7280a_devaddr(dev) << 8 | ch;
- i = dev * AD7280A_CELLS_PER_DEV + ch;
-
- ret = ad7280_balance_switch_attr_init(iio_attr, sdev, addr, i);
- if (ret < 0)
- return ret;
-
- ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr;
-
- (*cnt)++;
- iio_attr = &st->iio_attr[*cnt];
- addr = ad7280a_devaddr(dev) << 8 | (AD7280A_CB1_TIMER + ch);
-
- ret = ad7280_balance_timer_attr_init(iio_attr, sdev, addr, i);
- if (ret < 0)
- return ret;
-
- ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr;
- (*cnt)++;
- }
-
- ad7280_attributes[*cnt] = NULL;
-
- return 0;
-}
-
-static int ad7280_attr_init(struct ad7280_state *st)
-{
- int dev, cnt = 0, ret;
-
- st->iio_attr = devm_kcalloc(&st->spi->dev, 2, sizeof(*st->iio_attr) *
- (st->slave_num + 1) * AD7280A_CELLS_PER_DEV,
- GFP_KERNEL);
- if (!st->iio_attr)
- return -ENOMEM;
-
- for (dev = 0; dev <= st->slave_num; dev++) {
- ret = ad7280_init_dev_attrs(st, dev, &cnt);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static ssize_t ad7280_read_channel_config(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct ad7280_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- unsigned int val;
-
- switch (this_attr->address) {
- case AD7280A_CELL_OVERVOLTAGE:
- val = 1000 + (st->cell_threshhigh * 1568) / 100;
- break;
- case AD7280A_CELL_UNDERVOLTAGE:
- val = 1000 + (st->cell_threshlow * 1568) / 100;
- break;
- case AD7280A_AUX_ADC_OVERVOLTAGE:
- val = (st->aux_threshhigh * 196) / 10;
- break;
- case AD7280A_AUX_ADC_UNDERVOLTAGE:
- val = (st->aux_threshlow * 196) / 10;
- break;
- default:
- return -EINVAL;
- }
-
- return sprintf(buf, "%u\n", val);
-}
-
-static ssize_t ad7280_write_channel_config(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct ad7280_state *st = iio_priv(indio_dev);
- struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-
- long val;
- int ret;
-
- ret = kstrtol(buf, 10, &val);
- if (ret)
- return ret;
-
- switch (this_attr->address) {
- case AD7280A_CELL_OVERVOLTAGE:
- case AD7280A_CELL_UNDERVOLTAGE:
- val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */
- break;
- case AD7280A_AUX_ADC_OVERVOLTAGE:
- case AD7280A_AUX_ADC_UNDERVOLTAGE:
- val = (val * 10) / 196; /* LSB 19.6mV */
- break;
- default:
- return -EFAULT;
- }
-
- val = clamp(val, 0L, 0xFFL);
-
- mutex_lock(&st->lock);
- switch (this_attr->address) {
- case AD7280A_CELL_OVERVOLTAGE:
- st->cell_threshhigh = val;
- break;
- case AD7280A_CELL_UNDERVOLTAGE:
- st->cell_threshlow = val;
- break;
- case AD7280A_AUX_ADC_OVERVOLTAGE:
- st->aux_threshhigh = val;
- break;
- case AD7280A_AUX_ADC_UNDERVOLTAGE:
- st->aux_threshlow = val;
- break;
- }
-
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
- this_attr->address, 1, val);
-
- mutex_unlock(&st->lock);
-
- return ret ? ret : len;
-}
-
-static irqreturn_t ad7280_event_handler(int irq, void *private)
-{
- struct iio_dev *indio_dev = private;
- struct ad7280_state *st = iio_priv(indio_dev);
- unsigned int *channels;
- int i, ret;
-
- channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL);
- if (!channels)
- return IRQ_HANDLED;
-
- ret = ad7280_read_all_channels(st, st->scan_cnt, channels);
- if (ret < 0)
- goto out;
-
- for (i = 0; i < st->scan_cnt; i++) {
- if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) {
- if (((channels[i] >> 11) & 0xFFF) >=
- st->cell_threshhigh) {
- u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
- IIO_EV_DIR_RISING,
- IIO_EV_TYPE_THRESH,
- 0, 0, 0);
- iio_push_event(indio_dev, tmp,
- iio_get_time_ns(indio_dev));
- } else if (((channels[i] >> 11) & 0xFFF) <=
- st->cell_threshlow) {
- u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
- IIO_EV_DIR_FALLING,
- IIO_EV_TYPE_THRESH,
- 0, 0, 0);
- iio_push_event(indio_dev, tmp,
- iio_get_time_ns(indio_dev));
- }
- } else {
- if (((channels[i] >> 11) & 0xFFF) >=
- st->aux_threshhigh) {
- u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING);
- iio_push_event(indio_dev, tmp,
- iio_get_time_ns(indio_dev));
- } else if (((channels[i] >> 11) & 0xFFF) <=
- st->aux_threshlow) {
- u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_FALLING);
- iio_push_event(indio_dev, tmp,
- iio_get_time_ns(indio_dev));
- }
- }
- }
-
-out:
- kfree(channels);
-
- return IRQ_HANDLED;
-}
-
-/* Note: No need to fix checkpatch warning that reads:
- * CHECK: spaces preferred around that '-' (ctx:VxV)
- * The function argument is stringified and doesn't need a fix
- */
-static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value,
- in_voltage-voltage_thresh_low_value,
- 0644,
- ad7280_read_channel_config,
- ad7280_write_channel_config,
- AD7280A_CELL_UNDERVOLTAGE);
-
-static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value,
- in_voltage-voltage_thresh_high_value,
- 0644,
- ad7280_read_channel_config,
- ad7280_write_channel_config,
- AD7280A_CELL_OVERVOLTAGE);
-
-static IIO_DEVICE_ATTR(in_temp_thresh_low_value,
- 0644,
- ad7280_read_channel_config,
- ad7280_write_channel_config,
- AD7280A_AUX_ADC_UNDERVOLTAGE);
-
-static IIO_DEVICE_ATTR(in_temp_thresh_high_value,
- 0644,
- ad7280_read_channel_config,
- ad7280_write_channel_config,
- AD7280A_AUX_ADC_OVERVOLTAGE);
-
-static struct attribute *ad7280_event_attributes[] = {
- &iio_dev_attr_in_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in_thresh_high_value.dev_attr.attr,
- &iio_dev_attr_in_temp_thresh_low_value.dev_attr.attr,
- &iio_dev_attr_in_temp_thresh_high_value.dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group ad7280_event_attrs_group = {
- .attrs = ad7280_event_attributes,
-};
-
-static int ad7280_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val,
- int *val2,
- long m)
-{
- struct ad7280_state *st = iio_priv(indio_dev);
- int ret;
-
- switch (m) {
- case IIO_CHAN_INFO_RAW:
- mutex_lock(&st->lock);
- if (chan->address == AD7280A_ALL_CELLS)
- ret = ad7280_read_all_channels(st, st->scan_cnt, NULL);
- else
- ret = ad7280_read_channel(st, chan->address >> 8,
- chan->address & 0xFF);
- mutex_unlock(&st->lock);
-
- if (ret < 0)
- return ret;
-
- *val = ret;
-
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6)
- *val = 4000;
- else
- *val = 5000;
-
- *val2 = AD7280A_BITS;
- return IIO_VAL_FRACTIONAL_LOG2;
- }
- return -EINVAL;
-}
-
-static const struct iio_info ad7280_info = {
- .read_raw = ad7280_read_raw,
- .event_attrs = &ad7280_event_attrs_group,
- .attrs = &ad7280_attrs_group,
-};
-
-static const struct ad7280_platform_data ad7793_default_pdata = {
- .acquisition_time = AD7280A_ACQ_TIME_400ns,
- .conversion_averaging = AD7280A_CONV_AVG_DIS,
- .thermistor_term_en = true,
-};
-
-static int ad7280_probe(struct spi_device *spi)
-{
- const struct ad7280_platform_data *pdata = dev_get_platdata(&spi->dev);
- struct ad7280_state *st;
- int ret;
- const unsigned short t_acq_ns[4] = {465, 1010, 1460, 1890};
- const unsigned short n_avg[4] = {1, 2, 4, 8};
- struct iio_dev *indio_dev;
-
- indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
- if (!indio_dev)
- return -ENOMEM;
-
- st = iio_priv(indio_dev);
- spi_set_drvdata(spi, indio_dev);
- st->spi = spi;
- mutex_init(&st->lock);
-
- if (!pdata)
- pdata = &ad7793_default_pdata;
-
- crc8_populate_msb(st->crc_tab, POLYNOM);
-
- st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ;
- st->spi->mode = SPI_MODE_1;
- spi_setup(st->spi);
-
- st->ctrl_lb = AD7280A_CTRL_LB_ACQ_TIME(pdata->acquisition_time & 0x3);
- st->ctrl_hb = AD7280A_CTRL_HB_CONV_AVG(pdata->conversion_averaging
- & 0x3) | (pdata->thermistor_term_en ?
- AD7280A_CTRL_LB_THERMISTOR_EN : 0);
-
- ret = ad7280_chain_setup(st);
- if (ret < 0)
- return ret;
-
- st->slave_num = ret;
- st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH;
- st->cell_threshhigh = 0xFF;
- st->aux_threshhigh = 0xFF;
-
- ret = devm_add_action_or_reset(&spi->dev, ad7280_sw_power_down, st);
- if (ret)
- return ret;
-
- /*
- * Total Conversion Time = ((tACQ + tCONV) *
- * (Number of Conversions per Part)) −
- * tACQ + ((N - 1) * tDELAY)
- *
- * Readback Delay = Total Conversion Time + tWAIT
- */
-
- st->readback_delay_us =
- ((t_acq_ns[pdata->acquisition_time & 0x3] + 695) *
- (AD7280A_NUM_CH * n_avg[pdata->conversion_averaging & 0x3])) -
- t_acq_ns[pdata->acquisition_time & 0x3] + st->slave_num * 250;
-
- /* Convert to usecs */
- st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000);
- st->readback_delay_us += 5; /* Add tWAIT */
-
- indio_dev->name = spi_get_device_id(spi)->name;
- indio_dev->modes = INDIO_DIRECT_MODE;
-
- ret = ad7280_channel_init(st);
- if (ret < 0)
- return ret;
-
- indio_dev->num_channels = ret;
- indio_dev->channels = st->channels;
- indio_dev->info = &ad7280_info;
-
- ret = ad7280_attr_init(st);
- if (ret < 0)
- return ret;
-
- ret = devm_iio_device_register(&spi->dev, indio_dev);
- if (ret)
- return ret;
-
- if (spi->irq > 0) {
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
- AD7280A_ALERT, 1,
- AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN);
- if (ret)
- return ret;
-
- ret = ad7280_write(st, ad7280a_devaddr(st->slave_num),
- AD7280A_ALERT, 0,
- AD7280A_ALERT_GEN_STATIC_HIGH |
- (pdata->chain_last_alert_ignore & 0xF));
- if (ret)
- return ret;
-
- ret = devm_request_threaded_irq(&spi->dev, spi->irq,
- NULL,
- ad7280_event_handler,
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- indio_dev->name,
- indio_dev);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct spi_device_id ad7280_id[] = {
- {"ad7280a", 0},
- {}
-};
-MODULE_DEVICE_TABLE(spi, ad7280_id);
-
-static struct spi_driver ad7280_driver = {
- .driver = {
- .name = "ad7280",
- },
- .probe = ad7280_probe,
- .id_table = ad7280_id,
-};
-module_spi_driver(ad7280_driver);
-
-MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
-MODULE_DESCRIPTION("Analog Devices AD7280A");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7280a.h b/drivers/staging/iio/adc/ad7280a.h
deleted file mode 100644
index 23f18bb9e279..000000000000
--- a/drivers/staging/iio/adc/ad7280a.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * AD7280A Lithium Ion Battery Monitoring System
- *
- * Copyright 2011 Analog Devices Inc.
- */
-
-#ifndef IIO_ADC_AD7280_H_
-#define IIO_ADC_AD7280_H_
-
-/*
- * TODO: struct ad7280_platform_data needs to go into include/linux/iio
- */
-
-#define AD7280A_ACQ_TIME_400ns 0
-#define AD7280A_ACQ_TIME_800ns 1
-#define AD7280A_ACQ_TIME_1200ns 2
-#define AD7280A_ACQ_TIME_1600ns 3
-
-#define AD7280A_CONV_AVG_DIS 0
-#define AD7280A_CONV_AVG_2 1
-#define AD7280A_CONV_AVG_4 2
-#define AD7280A_CONV_AVG_8 3
-
-#define AD7280A_ALERT_REMOVE_VIN5 BIT(2)
-#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3)
-#define AD7280A_ALERT_REMOVE_AUX5 BIT(0)
-#define AD7280A_ALERT_REMOVE_AUX4_AUX5 BIT(1)
-
-struct ad7280_platform_data {
- unsigned int acquisition_time;
- unsigned int conversion_averaging;
- unsigned int chain_last_alert_ignore;
- bool thermistor_term_en;
-};
-
-#endif /* IIO_ADC_AD7280_H_ */
diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c
index 3a5336913cca..b3f310389378 100644
--- a/drivers/thunderbolt/nvm.c
+++ b/drivers/thunderbolt/nvm.c
@@ -154,10 +154,8 @@ int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
void tb_nvm_free(struct tb_nvm *nvm)
{
if (nvm) {
- if (nvm->non_active)
- nvmem_unregister(nvm->non_active);
- if (nvm->active)
- nvmem_unregister(nvm->active);
+ nvmem_unregister(nvm->non_active);
+ nvmem_unregister(nvm->active);
vfree(nvm->buf);
ida_simple_remove(&nvm_ida, nvm->id);
}
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
index 8ca1a235d164..60651a50770f 100644
--- a/drivers/usb/host/xhci-mvebu.c
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -8,7 +8,6 @@
#include <linux/mbus.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/phy/phy.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -75,47 +74,6 @@ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
return 0;
}
-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
-{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct device *dev = hcd->self.controller;
- struct phy *phy;
- int ret;
-
- /* Old bindings miss the PHY handle */
- phy = of_phy_get(dev->of_node, "usb3-phy");
- if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- else if (IS_ERR(phy))
- goto phy_out;
-
- ret = phy_init(phy);
- if (ret)
- goto phy_put;
-
- ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS);
- if (ret)
- goto phy_exit;
-
- ret = phy_power_on(phy);
- if (ret == -EOPNOTSUPP) {
- /* Skip initializatin of XHCI PHY when it is unsupported by firmware */
- dev_warn(dev, "PHY unsupported by firmware\n");
- xhci->quirks |= XHCI_SKIP_PHY_INIT;
- }
- if (ret)
- goto phy_exit;
-
- phy_power_off(phy);
-phy_exit:
- phy_exit(phy);
-phy_put:
- of_phy_put(phy);
-phy_out:
-
- return 0;
-}
-
int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h
index 01bf3fcb3eca..3be021793cc8 100644
--- a/drivers/usb/host/xhci-mvebu.h
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -12,7 +12,6 @@ struct usb_hcd;
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd);
int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
#else
static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
@@ -20,11 +19,6 @@ static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
return 0;
}
-static inline int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
-{
- return 0;
-}
-
static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
{
return 0;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 8094da34825e..649ffd861b44 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -44,16 +44,6 @@ static void xhci_priv_plat_start(struct usb_hcd *hcd)
priv->plat_start(hcd);
}
-static int xhci_priv_plat_setup(struct usb_hcd *hcd)
-{
- struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
-
- if (!priv->plat_setup)
- return 0;
-
- return priv->plat_setup(hcd);
-}
-
static int xhci_priv_init_quirk(struct usb_hcd *hcd)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
@@ -121,7 +111,6 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada = {
};
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
- .plat_setup = xhci_mvebu_a3700_plat_setup,
.init_quirk = xhci_mvebu_a3700_init_quirk,
};
@@ -334,14 +323,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
xhci->shared_hcd->tpl_support = hcd->tpl_support;
-
- if (priv) {
- ret = xhci_priv_plat_setup(hcd);
- if (ret)
- goto disable_usb_phy;
- }
-
- if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)))
+ if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
hcd->skip_phy_initialization = 1;
if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 561d0b7bce09..1fb149d1fbce 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -13,7 +13,6 @@
struct xhci_plat_priv {
const char *firmware_name;
unsigned long long quirks;
- int (*plat_setup)(struct usb_hcd *);
void (*plat_start)(struct usb_hcd *);
int (*init_quirk)(struct usb_hcd *);
int (*suspend_quirk)(struct usb_hcd *);
diff --git a/drivers/virt/acrn/hsm.c b/drivers/virt/acrn/hsm.c
index 5419794fccf1..423ea888d79a 100644
--- a/drivers/virt/acrn/hsm.c
+++ b/drivers/virt/acrn/hsm.c
@@ -136,8 +136,10 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd,
if (IS_ERR(vm_param))
return PTR_ERR(vm_param);
- if ((vm_param->reserved0 | vm_param->reserved1) != 0)
+ if ((vm_param->reserved0 | vm_param->reserved1) != 0) {
+ kfree(vm_param);
return -EINVAL;
+ }
vm = acrn_vm_create(vm, vm_param);
if (!vm) {
@@ -182,21 +184,29 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd,
return PTR_ERR(cpu_regs);
for (i = 0; i < ARRAY_SIZE(cpu_regs->reserved); i++)
- if (cpu_regs->reserved[i])
+ if (cpu_regs->reserved[i]) {
+ kfree(cpu_regs);
return -EINVAL;
+ }
for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.reserved_32); i++)
- if (cpu_regs->vcpu_regs.reserved_32[i])
+ if (cpu_regs->vcpu_regs.reserved_32[i]) {
+ kfree(cpu_regs);
return -EINVAL;
+ }
for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.reserved_64); i++)
- if (cpu_regs->vcpu_regs.reserved_64[i])
+ if (cpu_regs->vcpu_regs.reserved_64[i]) {
+ kfree(cpu_regs);
return -EINVAL;
+ }
for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.gdt.reserved); i++)
if (cpu_regs->vcpu_regs.gdt.reserved[i] |
- cpu_regs->vcpu_regs.idt.reserved[i])
+ cpu_regs->vcpu_regs.idt.reserved[i]) {
+ kfree(cpu_regs);
return -EINVAL;
+ }
ret = hcall_set_vcpu_regs(vm->vmid, virt_to_phys(cpu_regs));
if (ret < 0)
diff --git a/drivers/virt/acrn/irqfd.c b/drivers/virt/acrn/irqfd.c
index df5184979b28..d4ad211dce7a 100644
--- a/drivers/virt/acrn/irqfd.c
+++ b/drivers/virt/acrn/irqfd.c
@@ -17,7 +17,6 @@
#include "acrn_drv.h"
static LIST_HEAD(acrn_irqfd_clients);
-static DEFINE_MUTEX(acrn_irqfds_mutex);
/**
* struct hsm_irqfd - Properties of HSM irqfd
diff --git a/drivers/virt/acrn/mm.c b/drivers/virt/acrn/mm.c
index c4f2e15c8a2b..3b1b1e7a844b 100644
--- a/drivers/virt/acrn/mm.c
+++ b/drivers/virt/acrn/mm.c
@@ -162,10 +162,34 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
void *remap_vaddr;
int ret, pinned;
u64 user_vm_pa;
+ unsigned long pfn;
+ struct vm_area_struct *vma;
if (!vm || !memmap)
return -EINVAL;
+ mmap_read_lock(current->mm);
+ vma = vma_lookup(current->mm, memmap->vma_base);
+ if (vma && ((vma->vm_flags & VM_PFNMAP) != 0)) {
+ if ((memmap->vma_base + memmap->len) > vma->vm_end) {
+ mmap_read_unlock(current->mm);
+ return -EINVAL;
+ }
+
+ ret = follow_pfn(vma, memmap->vma_base, &pfn);
+ mmap_read_unlock(current->mm);
+ if (ret < 0) {
+ dev_dbg(acrn_dev.this_device,
+ "Failed to lookup PFN at VMA:%pK.\n", (void *)memmap->vma_base);
+ return ret;
+ }
+
+ return acrn_mm_region_add(vm, memmap->user_vm_pa,
+ PFN_PHYS(pfn), memmap->len,
+ ACRN_MEM_TYPE_WB, memmap->attr);
+ }
+ mmap_read_unlock(current->mm);
+
/* Get the page number of the map region */
nr_pages = memmap->len >> PAGE_SHIFT;
pages = vzalloc(nr_pages * sizeof(struct page *));
diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c
index 46ee0a0998b6..e49bec8bc8a4 100644
--- a/drivers/virt/fsl_hypervisor.c
+++ b/drivers/virt/fsl_hypervisor.c
@@ -687,15 +687,13 @@ static int fsl_hv_close(struct inode *inode, struct file *filp)
struct doorbell_queue *dbq = filp->private_data;
unsigned long flags;
- int ret = 0;
-
spin_lock_irqsave(&db_list_lock, flags);
list_del(&dbq->list);
spin_unlock_irqrestore(&db_list_lock, flags);
kfree(dbq);
- return ret;
+ return 0;
}
static const struct file_operations fsl_hv_fops = {
diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c
index cd8821580f71..f6664fc9596a 100644
--- a/drivers/w1/masters/ds2490.c
+++ b/drivers/w1/masters/ds2490.c
@@ -219,10 +219,8 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count)
{
int i;
- pr_info("0x%x: count=%d, status: ", dev->ep[EP_STATUS], count);
- for (i = 0; i < count; ++i)
- pr_info("%02x ", buf[i]);
- pr_info("\n");
+ dev_info(&dev->udev->dev, "ep_status=0x%x, count=%d, status=%*phC",
+ dev->ep[EP_STATUS], count, count, buf);
if (count >= 16) {
ds_print_msg(buf, "enable flag", 0);
@@ -331,7 +329,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
buf, size, &count, 1000);
if (err < 0) {
- pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
+ dev_info(&dev->udev->dev, "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]));
ds_recv_status(dev, NULL, true);
return err;
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 565578002d79..067692626cf0 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -574,6 +574,41 @@ static inline int w1_DS18S20_convert_time(struct w1_slave *sl)
return SLAVE_CONV_TIME_OVERRIDE(sl);
}
+static inline int w1_DS1825_convert_time(struct w1_slave *sl)
+{
+ int ret;
+
+ if (!sl->family_data)
+ return -ENODEV; /* device unknown */
+
+ if (SLAVE_CONV_TIME_OVERRIDE(sl) != CONV_TIME_DEFAULT)
+ return SLAVE_CONV_TIME_OVERRIDE(sl);
+
+ /* Return the conversion time, depending on resolution,
+ * select maximum conversion time among all compatible devices
+ */
+ switch (SLAVE_RESOLUTION(sl)) {
+ case 9:
+ ret = 95;
+ break;
+ case 10:
+ ret = 190;
+ break;
+ case 11:
+ ret = 375;
+ break;
+ case 12:
+ ret = 750;
+ break;
+ case 14:
+ ret = 100; /* MAX31850 only. Datasheet says 100ms */
+ break;
+ default:
+ ret = 750;
+ }
+ return ret;
+}
+
static inline int w1_DS18B20_write_data(struct w1_slave *sl,
const u8 *data)
{
@@ -594,6 +629,7 @@ static inline int w1_DS18B20_set_resolution(struct w1_slave *sl, int val)
/* DS18B20 resolution is 9 to 12 bits */
/* GX20MH01 resolution is 9 to 14 bits */
+ /* MAX31850 resolution is fixed 14 bits */
if (val < W1_THERM_RESOLUTION_MIN || val > W1_THERM_RESOLUTION_MAX)
return -EINVAL;
@@ -649,6 +685,7 @@ static inline int w1_DS18B20_get_resolution(struct w1_slave *sl)
+ W1_THERM_RESOLUTION_MIN;
/* GX20MH01 has one special case:
* >=14 means 14 bits when getting resolution from bit value.
+ * MAX31850 delivers fixed 15 and has 14 bits.
* Other devices have no more then 12 bits.
*/
if (resolution > W1_THERM_RESOLUTION_MAX)
@@ -715,6 +752,34 @@ static inline int w1_DS18S20_convert_temp(u8 rom[9])
return t;
}
+/**
+ * w1_DS1825_convert_temp() - temperature computation for DS1825
+ * @rom: data read from device RAM (8 data bytes + 1 CRC byte)
+ *
+ * Can be called for any DS1825 compliant device.
+ * Is used by MAX31850, too
+ *
+ * Return: value in millidegrees Celsius.
+ */
+
+static inline int w1_DS1825_convert_temp(u8 rom[9])
+{
+ u16 bv;
+ s16 t;
+
+ /* Signed 16-bit value to unsigned, cpu order */
+ bv = le16_to_cpup((__le16 *)rom);
+
+ /* Config register bit 7 = 1 - MA31850 found, 14 bit resolution */
+ if (rom[4] & 0x80) {
+ /* Mask out bits 0 (Fault) and 1 (Reserved) */
+ /* Avoid arithmetic shift of signed value */
+ bv = (bv & 0xFFFC); /* Degrees, lowest 4 bits are 2^-1, 2^-2 and 2 zero bits */
+ }
+ t = (s16)bv; /* Degrees, lowest bit is 2^-4 */
+ return (int)t * 1000 / 16; /* Sign-extend to int; millidegrees */
+}
+
/* Device capability description */
/* GX20MH01 device shares family number and structure with DS18B20 */
@@ -757,9 +822,10 @@ static struct w1_therm_family_converter w1_therm_families[] = {
.bulk_read = false
},
{
+ /* Also used for MAX31850 */
.f = &w1_therm_family_DS1825,
- .convert = w1_DS18B20_convert_temp,
- .get_conversion_time = w1_DS18B20_convert_time,
+ .convert = w1_DS1825_convert_temp,
+ .get_conversion_time = w1_DS1825_convert_time,
.set_resolution = w1_DS18B20_set_resolution,
.get_resolution = w1_DS18B20_get_resolution,
.write_data = w1_DS18B20_write_data,
@@ -2089,16 +2155,20 @@ static ssize_t w1_seq_show(struct device *device,
if (sl->reg_num.id == reg_num->id)
seq = i;
+ if (w1_reset_bus(sl->master))
+ goto error;
+
+ /* Put the device into chain DONE state */
+ w1_write_8(sl->master, W1_MATCH_ROM);
+ w1_write_block(sl->master, (u8 *)&rn, 8);
w1_write_8(sl->master, W1_42_CHAIN);
w1_write_8(sl->master, W1_42_CHAIN_DONE);
w1_write_8(sl->master, W1_42_CHAIN_DONE_INV);
- w1_read_block(sl->master, &ack, sizeof(ack));
/* check for acknowledgment */
ack = w1_read_8(sl->master);
if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
goto error;
-
}
/* Exit from CHAIN state */