IIO: 1st set of new device support, features and cleanup for 6.12

Includes a merge of spi-mos-config branch from spi.git that brings
 support needed for the AD4000 driver.
 
 Lots of new device support this time including 9 new drivers and substantial
 changes to add new support to several more.
 
 New device support
 ------------------
 
 Given we have a lot of new support, I've subcategorized them:
 
 Substantial changes, or new driver
 **********************************
 
 adi,ad4000
 - New driver for this high speed ADC.
 adi,ad4695
 - New driver supporting AD4690, AD4696, AD4697 and AD4698 ADCs.
 - Follow up series added triggered buffer support.
 adi,ad7380
 - Add support for single ended parts, AD7386, ADC7387, AD7388 and -4 variants.
   (driver previously only support differential parts).
   These variants have an additional front end MUX so only half the channels
   can be sampled efficiently.
 adi,ad9467
 - Refactor and extend driver to support ad9643, ad9449 and ad9652 high speed
   ADCs.
 adi,adxl380
 - New driver for this low power accelerometer.
 adi,ltc2664
 - New driver supporting LTC2664 and LTC2672 DACs.
 microchip,pac1921
 - New driver for this power/current monitor chip.
 rohm,bh1745
 - New driver for this RGBC colour sensor.
 rohm,bu27034anuc
 - The original bu27034 was canceled before mass production, so the
   driver is modified to support the BU27034ANUC which had some significant
   differences.  DT compatible changed to avoid chance of old driver ever
   binding to real hardware.
 sciosense,ens210
 - New driver for ens210, ens210a, ens211, ens212, ens213a, and ens215
   temperature and humidity sensors (all register compatible up to some
   conversion time differences)
 sensiron,sdp500
 - New driver for this differential pressure sensor.
 tyhx,hx9023s
 - New driver to support this capacitive proximity sensor.
 
 Minor changes to support new devices
 ************************************
 
 adi,adf4377
 - Add support for the single output adf4378.
 kionix,kxcjk-1013
 - Add support for KX022-1020 accelerometer (binding and ID table only)
 liteon,ltrf216a
 - Add support for ltr-308.  A few minor differences in features set
 rockchip,saradc
 - Add ID for rk3576-saradc
 sensortek,stk3310
 - Add ID for stk3013 proximity sensor which (despite documentation) has
   an ambient light sensor and is compatible with existing parts.
 
 Documentation updates
 ---------------------
 
 Generalize ABI docs for shunt resistor attribute
 Improve calibscale and calibbias related documentation.  A couple of follow
 up patches to resolve duplicate documentation that resulted.
 
 New core features
 -----------------
 
 backend
 - Add option for debugfs - useful for test pattern control
 - Use this for both adi-axi-adc and adi-axi-dac
 trigger suspend
 - Add functions to allow triggers to be suspended. This avoids problems
   when a device enters suspend to idle with a sysfs trigger. Use it for now
   in the bmi323 only.
 
 New driver features
 -------------------
 
 adi,ad7192
 - Add option to be a clock provider (+ additional clock config options)
 adi,ad7380
 - Add documentation for this fairly new driver.
 adi,ad9461
 - Provide control of test modes and backend validation blocks used
   to identify problems (via debugfs)
 adi,ad9739
 - Add backend debugfs and docs for what is provided via adi-axi-dac
 avago,apds9960
 - Add proximity and gesture calibration offset control
 bosch,bmp280
 - Triggered buffer support including adding raw+scale output for sysfs.
 liteon,ltr390
 - Add configuration of integration time and scale.
 stm,dfsdm
 - Convert this SD modulator driver to backend framework and add support
   for channel scaling + modern channel bindings.
 
 Treewide cleanup
 ----------------
 
 iio_dev->masklength: Making it private.
 - Provide access function to read the core compute channel mask length
   and a macro to iterate over elements in the active_scan_mask.
 - Enables marking masklength __private preventing drivers from
   writing it without triggering a build warning whilst minimizing overhead
   in what are typically hot paths.
 - Convert all drivers and finally mark it private.
   Merge conflicts resolved in drivers applied after this point.
 Constify regmap_bus
 - These are never modified, so mark them const.
 
 Core cleanup
 ------------
 
 backend
 - A few late breaking bits of feedback (unused variable, error messages)
 dma-buffer
 - Namespace exports.
 core
 - Drop unused assignment.
 
 Driver cleanup
 --------------
 
 adi,ad4695
 - Fixing binding to reflect that common-mode-channel is a scalar.
 adi,ad7280a
 - Use __free(kfree) to simplify freeing of receive buffer.
 adi,ad7606
 - Various dt-binding cleanup and improvements.
 - Fix oversampling related gpio handling.
 - Make polarity of standby gpio match documentation.
 - use guard() to simplify lock handling.
 adi,ad7768
 - Use device_for_each_child_node_scoped() instead of fwnode equivalent.
 adi,ad7124
 - Reduce SPI transfers by avoiding separate writes to different fields
   in the same register.
 - Start the ADC in idle mode.
 adi,adis
 - Drop ifdefs in favor of IS_ENABLED.
 adi,admv8818
 - Fix wrong ABI docs.
 asahi-kasei,ak8975
 - Drop a prefix free compatible accidentally added recently.
 aspeed,adc
 - Use of_property_present() instead of of_find_property() to see if the
   property is there or not.
 atmel,at91,
 - Use __free(kfree) to simplify freeing of channel related array.
 bosch,bma400
 - Use __free(kfree) to simplify freeing a locally allocated string.
 bosch,bmc150
 - Add missing mount-matrix binding docs.
 bosch,bme680
 - Fix read/write to ensure multiple necessary sequential reads without
   device configuration change.
 - Drop unnecessary type casts and use more appropriate data types.
 - Drop some left over ACPI code as ACPI support was removed due to invalid
   IDs (and no known users).
 - Sort headers consistently.
 - Avoid unnecessary duplicate read and redundant read of gas config.
 - Use bulk reads to get calibration data.
 - Reorder allocation of IIO device to be prior to device init.
 - Add remaining read/write buffers to the union used already for all others.
 - Tidy up error checks for consistency of style, including dev_err_probe()
 - Bring the device startup procedure inline with the vendor code.
 - Reorder code so mode forcing is more obvious occurring where needed.
 - Tidy up data locality in reading functions so no magic data is stored
   in state structures just to get it across function calls.
 - Make a local lookup table static to avoid placing it on the stack.
 bosch,bmp280
 - Fix BME280 regmap to not include registers it doesn't have.
 - Wait a little longer after config to allow for maximum possible necessary
   wait.
 - Reorganize headers.
 - Make conversion_time_max array static to avoid placing it on the stack.
 maxim,max1363
 - Use __free(kfree) to simplify freeing transmission buffer.
 microchip,mcp3964
 - Use devm_regulator_get_enable_read_voltage()
 microchip,mcp3911
 - Use devm_regulator_get_enable_read_voltage()
 microchip,mcp4728
 - Use devm_regulator_get_enable_read_voltage()
 microchip,mcp4922
 - Use devm_regulator_get_enable_read_voltage() and devm_* to allow
   dropping of explicit remove() callback.
 onnn,noa1305
 - Various tidy up.
 - Provide available scale values.
 - Make integration time configurable.
 - Fix up integration time look up (/2 error)
 ti,dac7311
 - Check if spi_setup() succeeded.
 ti,tsc2046
 - Use __free(kfree) to simplify freeing rx and tx buffers.
 - Use devm_regulator_get_enable_read_voltage()
 
 Various minor fixes not called out explicitly.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmbHhnARHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0FohpshAApFDoTkyYMa7x1r5WUZ/5j474319LvwDO
 /9UIDIgR8qSzR2fDYl+LR03ZWknsOXF4lfCrCf65zPaR/8bB7TsjD8A7uIPVAKDF
 Tu+nSgBworJcvokPzygtrjoor2u2LCXdZVurYrFggMZ833LY5HTotFDAB32wx3QM
 p7p7OU0LgAZ8VR+ykzkbwp9NjOSrgD2mD7emy7Enu4h/OzLzst0c15KkUaOpnSUZ
 8R/+tz5lERrF+ACjWm+sWSe8ry2SkQppd8G8pSXyUM0uD2KO0I78FEpA3wUB2H++
 wiki1cm1kOM/ljHbXn2tqp5s+A8p6d0/LOCZm9bUi9kmtP5J2ky2iZmpZPraO52d
 +jbnHh/GyvoyIzeZRJZtp9h4hWTPNV2pgvb5BHD7Fek5rxOXXBlulDd695Ygbfq5
 vxiXYfN+ozVQk3/1mm0FwA34VZSoHADvzTxANQE9Vi99ywenpqJ5VYWQm/Bf0oHm
 HMH1sCcrmPHF9NOEUPV2uCanTQ20Q+OO89xOUBDGma1FKh6108wSont5c6GX/dKu
 sChUdllXSlNUR8VoiAYSFEP/U+gXnRE8Scxuk1Xx12RuKYpe0NNdRyRtj86kTU+1
 e6gHY90NskQCSVvOiivvo/rNTO08EZND9V3pbD/2HxaFvM4zw/iJtNR49DLdTkpX
 DfiCl2BAbLw=
 =eXUA
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-6.12a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-testing

Jonathan writes:

IIO: 1st set of new device support, features and cleanup for 6.12

Includes a merge of spi-mos-config branch from spi.git that brings
support needed for the AD4000 driver.

Lots of new device support this time including 9 new drivers and substantial
changes to add new support to several more.

New device support
------------------

Given we have a lot of new support, I've subcategorized them:

Substantial changes, or new driver
**********************************

adi,ad4000
- New driver for this high speed ADC.
adi,ad4695
- New driver supporting AD4690, AD4696, AD4697 and AD4698 ADCs.
- Follow up series added triggered buffer support.
adi,ad7380
- Add support for single ended parts, AD7386, ADC7387, AD7388 and -4 variants.
  (driver previously only support differential parts).
  These variants have an additional front end MUX so only half the channels
  can be sampled efficiently.
adi,ad9467
- Refactor and extend driver to support ad9643, ad9449 and ad9652 high speed
  ADCs.
adi,adxl380
- New driver for this low power accelerometer.
adi,ltc2664
- New driver supporting LTC2664 and LTC2672 DACs.
microchip,pac1921
- New driver for this power/current monitor chip.
rohm,bh1745
- New driver for this RGBC colour sensor.
rohm,bu27034anuc
- The original bu27034 was canceled before mass production, so the
  driver is modified to support the BU27034ANUC which had some significant
  differences.  DT compatible changed to avoid chance of old driver ever
  binding to real hardware.
sciosense,ens210
- New driver for ens210, ens210a, ens211, ens212, ens213a, and ens215
  temperature and humidity sensors (all register compatible up to some
  conversion time differences)
sensiron,sdp500
- New driver for this differential pressure sensor.
tyhx,hx9023s
- New driver to support this capacitive proximity sensor.

Minor changes to support new devices
************************************

adi,adf4377
- Add support for the single output adf4378.
kionix,kxcjk-1013
- Add support for KX022-1020 accelerometer (binding and ID table only)
liteon,ltrf216a
- Add support for ltr-308.  A few minor differences in features set
rockchip,saradc
- Add ID for rk3576-saradc
sensortek,stk3310
- Add ID for stk3013 proximity sensor which (despite documentation) has
  an ambient light sensor and is compatible with existing parts.

Documentation updates
---------------------

Generalize ABI docs for shunt resistor attribute
Improve calibscale and calibbias related documentation.  A couple of follow
up patches to resolve duplicate documentation that resulted.

New core features
-----------------

backend
- Add option for debugfs - useful for test pattern control
- Use this for both adi-axi-adc and adi-axi-dac
trigger suspend
- Add functions to allow triggers to be suspended. This avoids problems
  when a device enters suspend to idle with a sysfs trigger. Use it for now
  in the bmi323 only.

New driver features
-------------------

adi,ad7192
- Add option to be a clock provider (+ additional clock config options)
adi,ad7380
- Add documentation for this fairly new driver.
adi,ad9461
- Provide control of test modes and backend validation blocks used
  to identify problems (via debugfs)
adi,ad9739
- Add backend debugfs and docs for what is provided via adi-axi-dac
avago,apds9960
- Add proximity and gesture calibration offset control
bosch,bmp280
- Triggered buffer support including adding raw+scale output for sysfs.
liteon,ltr390
- Add configuration of integration time and scale.
stm,dfsdm
- Convert this SD modulator driver to backend framework and add support
  for channel scaling + modern channel bindings.

Treewide cleanup
----------------

iio_dev->masklength: Making it private.
- Provide access function to read the core compute channel mask length
  and a macro to iterate over elements in the active_scan_mask.
- Enables marking masklength __private preventing drivers from
  writing it without triggering a build warning whilst minimizing overhead
  in what are typically hot paths.
- Convert all drivers and finally mark it private.
  Merge conflicts resolved in drivers applied after this point.
Constify regmap_bus
- These are never modified, so mark them const.

Core cleanup
------------

backend
- A few late breaking bits of feedback (unused variable, error messages)
dma-buffer
- Namespace exports.
core
- Drop unused assignment.

Driver cleanup
--------------

adi,ad4695
- Fixing binding to reflect that common-mode-channel is a scalar.
adi,ad7280a
- Use __free(kfree) to simplify freeing of receive buffer.
adi,ad7606
- Various dt-binding cleanup and improvements.
- Fix oversampling related gpio handling.
- Make polarity of standby gpio match documentation.
- use guard() to simplify lock handling.
adi,ad7768
- Use device_for_each_child_node_scoped() instead of fwnode equivalent.
adi,ad7124
- Reduce SPI transfers by avoiding separate writes to different fields
  in the same register.
- Start the ADC in idle mode.
adi,adis
- Drop ifdefs in favor of IS_ENABLED.
adi,admv8818
- Fix wrong ABI docs.
asahi-kasei,ak8975
- Drop a prefix free compatible accidentally added recently.
aspeed,adc
- Use of_property_present() instead of of_find_property() to see if the
  property is there or not.
atmel,at91,
- Use __free(kfree) to simplify freeing of channel related array.
bosch,bma400
- Use __free(kfree) to simplify freeing a locally allocated string.
bosch,bmc150
- Add missing mount-matrix binding docs.
bosch,bme680
- Fix read/write to ensure multiple necessary sequential reads without
  device configuration change.
- Drop unnecessary type casts and use more appropriate data types.
- Drop some left over ACPI code as ACPI support was removed due to invalid
  IDs (and no known users).
- Sort headers consistently.
- Avoid unnecessary duplicate read and redundant read of gas config.
- Use bulk reads to get calibration data.
- Reorder allocation of IIO device to be prior to device init.
- Add remaining read/write buffers to the union used already for all others.
- Tidy up error checks for consistency of style, including dev_err_probe()
- Bring the device startup procedure inline with the vendor code.
- Reorder code so mode forcing is more obvious occurring where needed.
- Tidy up data locality in reading functions so no magic data is stored
  in state structures just to get it across function calls.
- Make a local lookup table static to avoid placing it on the stack.
bosch,bmp280
- Fix BME280 regmap to not include registers it doesn't have.
- Wait a little longer after config to allow for maximum possible necessary
  wait.
- Reorganize headers.
- Make conversion_time_max array static to avoid placing it on the stack.
maxim,max1363
- Use __free(kfree) to simplify freeing transmission buffer.
microchip,mcp3964
- Use devm_regulator_get_enable_read_voltage()
microchip,mcp3911
- Use devm_regulator_get_enable_read_voltage()
microchip,mcp4728
- Use devm_regulator_get_enable_read_voltage()
microchip,mcp4922
- Use devm_regulator_get_enable_read_voltage() and devm_* to allow
  dropping of explicit remove() callback.
onnn,noa1305
- Various tidy up.
- Provide available scale values.
- Make integration time configurable.
- Fix up integration time look up (/2 error)
ti,dac7311
- Check if spi_setup() succeeded.
ti,tsc2046
- Use __free(kfree) to simplify freeing rx and tx buffers.
- Use devm_regulator_get_enable_read_voltage()

Various minor fixes not called out explicitly.

* tag 'iio-for-6.12a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (250 commits)
  drivers:iio:Fix the NULL vs IS_ERR() bug for debugfs_create_dir()
  iio: sgp40: retain documentation in driver
  iio: ABI: remove duplicate in_resistance_calibbias
  dt-bindings: iio: st,stm32-adc: add top-level constraints
  iio: ABI: add missing calibbias attributes
  iio: ABI: add missing calibscale attributes
  iio: ABI: sort calibscale attributes
  iio: ABI: document calibscale_available attributes
  iio: light: ltr390: Calculate 'counts_per_uvi' dynamically
  iio: light: ltr390: Add ALS channel and support for gain and resolution
  doc: iio: ad4695: document buffered read
  iio: adc: ad4695: implement triggered buffer
  iio: proximity: hx9023s: Fix error code in hx9023s_property_get()
  iio: light: noa1305: Fix up integration time look up
  iio: humidity: Add support for ENS210
  dt-bindings: iio: humidity: add ENS210 sensor family
  iio: imu: adis16460: drop ifdef around CONFIG_DEBUG_FS
  iio: imu: adis16400: drop ifdef around CONFIG_DEBUG_FS
  iio: imu: adis16480: drop ifdef around CONFIG_DEBUG_FS
  iio: imu: adis16475: drop ifdef around CONFIG_DEBUG_FS
  ...
This commit is contained in:
Greg Kroah-Hartman 2024-09-03 11:32:16 +02:00
commit f53835f110
197 changed files with 14744 additions and 1806 deletions

View File

@ -0,0 +1,39 @@
What: /sys/kernel/debug/iio/iio:deviceX/calibration_table_dump
KernelVersion: 6.11
Contact: linux-iio@vger.kernel.org
Description:
This dumps the calibration table that was filled during the
digital interface tuning process.
What: /sys/kernel/debug/iio/iio:deviceX/in_voltage_test_mode_available
KernelVersion: 6.11
Contact: linux-iio@vger.kernel.org
Description:
List all the available test tones:
- off
- midscale_short
- pos_fullscale
- neg_fullscale
- checkerboard
- prbs23
- prbs9
- one_zero_toggle
- user
- bit_toggle
- sync
- one_bit_high
- mixed_bit_frequency
- ramp
Note that depending on the actual device being used, some of the
above might not be available (and they won't be listed when
reading the file).
What: /sys/kernel/debug/iio/iio:deviceX/in_voltageY_test_mode
KernelVersion: 6.11
Contact: linux-iio@vger.kernel.org
Description:
Writing to this file will initiate one of available test tone on
channel Y. Reading it, shows which test is running. In cases
where an IIO backend is available and supports the test tone,
additional information about the data correctness is given.

View File

@ -0,0 +1,20 @@
What: /sys/kernel/debug/iio/iio:deviceX/backendY/name
KernelVersion: 6.11
Contact: linux-iio@vger.kernel.org
Description:
Name of Backend Y connected to device X.
What: /sys/kernel/debug/iio/iio:deviceX/backendY/direct_reg_access
KernelVersion: 6.11
Contact: linux-iio@vger.kernel.org
Description:
Directly access the registers of backend Y. Typical usage is:
Reading address 0x50
echo 0x50 > direct_reg_access
cat direct_reg_access
Writing address 0x50
echo 0x50 0x3 > direct_reg_access
//readback address 0x50
cat direct_reg_access

View File

@ -523,13 +523,26 @@ Description:
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_i_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_q_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_capacitance_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_proximity_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_resistance_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibbias
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibbias
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibbias
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -541,6 +554,9 @@ Description:
What: /sys/bus/iio/devices/iio:deviceX/in_accel_calibbias_available
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_calibbias_available
What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibbias_available
What: /sys/bus/iio/devices/iio:deviceX/in_proximity_calibbias_available
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibbias_available
KernelVersion: 5.8
Contact: linux-iio@vger.kernel.org
Description:
@ -549,25 +565,34 @@ Description:
- a small discrete set of values like "0 2 4 6 8"
- a range specified as "[min step max]"
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_capacitance_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_both_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibscale
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -575,6 +600,19 @@ Description:
production inaccuracies). If shared across all channels,
<type>_calibscale is used.
What: /sys/bus/iio/devices/iio:deviceX/in_illuminanceY_calibscale_available
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_calibscale_available
What: /sys/bus/iio/devices/iio:deviceX/in_proximityY_calibscale_available
KernelVersion: 4.8
Contact: linux-iio@vger.kernel.org
Description:
Available values of calibscale. Maybe expressed as either of:
- a small discrete set of values like "1 8 16"
- a range specified as "[min step max]"
If shared across all channels, <type>_calibscale_available is used.
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
@ -708,6 +746,7 @@ Description:
2.5kohm_to_gnd: connected to ground via a 2.5kOhm resistor,
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
42kohm_to_gnd: connected to ground via a 42kOhm resistor,
90kohm_to_gnd: connected to ground via a 90kOhm resistor,
100kohm_to_gnd: connected to ground via an 100kOhm resistor,
125kohm_to_gnd: connected to ground via an 125kOhm resistor,
@ -2289,3 +2328,11 @@ KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
List of available timeout value for tap gesture confirmation.
What: /sys/.../iio:deviceX/in_shunt_resistor
What: /sys/.../iio:deviceX/in_current_shunt_resistor
What: /sys/.../iio:deviceX/in_power_shunt_resistor
KernelVersion: 6.10
Contact: linux-iio@vger.kernel.org
Description:
The value of current sense resistor in Ohms.

View File

@ -1,17 +0,0 @@
What: /sys/bus/iio/devices/iio:deviceX/in_power_shunt_resistor
Date: March 2017
KernelVersion: 4.12
Contact: linux-iio@vger.kernel.org
Description: The value of the shunt resistor used to compute power drain on
common input voltage pin (RS+). In Ohms.
What: /sys/bus/iio/devices/iio:deviceX/in_current_shunt_resistor
Date: March 2017
KernelVersion: 4.12
Contact: linux-iio@vger.kernel.org
Description: The value of the shunt resistor used to compute current flowing
between RS+ and RS- voltage sense inputs. In Ohms.
These attributes describe a single physical component, exposed as two distinct
attributes as it is used to calculate two different values: power load and
current flowing between RS+ and RS- inputs.

View File

@ -15,17 +15,3 @@ Description:
Set the relative humidity. This value is sent to the sensor for
humidity compensation.
Default value: 50000 (50 % relative humidity)
What: /sys/bus/iio/devices/iio:deviceX/in_resistance_calibbias
Date: August 2021
KernelVersion: 5.15
Contact: Andreas Klinger <ak@it-klinger.de>
Description:
Set the bias value for the resistance which is used for
calculation of in_concentration_input as follows:
x = (in_resistance_raw - in_resistance_calibbias) * 0.65
in_concentration_input = 500 / (1 + e^x)
Default value: 30000

View File

@ -0,0 +1,61 @@
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_toggle_en
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Toggle enable. Write 1 to enable toggle or 0 to disable it. This
is useful when one wants to change the DAC output codes. For
autonomous toggling, the way it should be done is:
- disable toggle operation;
- change out_currentY_rawN, where N is the integer value of the symbol;
- enable toggle operation.
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_rawN
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
This attribute has the same meaning as out_currentY_raw. It is
specific to toggle enabled channels and refers to the DAC output
code in INPUT_N (_rawN), where N is the integer value of the symbol.
The same scale and offset as in out_currentY_raw applies.
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_symbol
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Performs a SW switch to a predefined output symbol. This attribute
is specific to toggle enabled channels and allows switching between
multiple predefined symbols. Each symbol corresponds to a different
output, denoted as out_currentY_rawN, where N is the integer value
of the symbol. Writing an integer value N will select out_currentY_rawN.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_toggle_en
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Toggle enable. Write 1 to enable toggle or 0 to disable it. This
is useful when one wants to change the DAC output codes. For
autonomous toggling, the way it should be done is:
- disable toggle operation;
- change out_voltageY_rawN, where N is the integer value of the symbol;
- enable toggle operation.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_rawN
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
This attribute has the same meaning as out_currentY_raw. It is
specific to toggle enabled channels and refers to the DAC output
code in INPUT_N (_rawN), where N is the integer value of the symbol.
The same scale and offset as in out_currentY_raw applies.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_symbol
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Performs a SW switch to a predefined output symbol. This attribute
is specific to toggle enabled channels and allows switching between
multiple predefined symbols. Each symbol corresponds to a different
output, denoted as out_voltageY_rawN, where N is the integer value
of the symbol. Writing an integer value N will select out_voltageY_rawN.

View File

@ -53,34 +53,3 @@ KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Returns the available values for the dither phase.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_toggle_en
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Toggle enable. Write 1 to enable toggle or 0 to disable it. This is
useful when one wants to change the DAC output codes. The way it should
be done is:
- disable toggle operation;
- change out_voltageY_raw0 and out_voltageY_raw1;
- enable toggle operation.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw0
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw1
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
It has the same meaning as out_voltageY_raw. This attribute is
specific to toggle enabled channels and refers to the DAC output
code in INPUT_A (_raw0) and INPUT_B (_raw1). The same scale and offset
as in out_voltageY_raw applies.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_symbol
KernelVersion: 5.18
Contact: linux-iio@vger.kernel.org
Description:
Performs a SW toggle. This attribute is specific to toggle
enabled channels and allows to toggle between out_voltageY_raw0
and out_voltageY_raw1 through software. Writing 0 will select
out_voltageY_raw0 while 1 selects out_voltageY_raw1.

View File

@ -3,7 +3,7 @@ KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Reading this returns the valid values that can be written to the
on_altvoltage0_mode attribute:
filter_mode attribute:
- auto -> Adjust bandpass filter to track changes in input clock rate.
- manual -> disable/unregister the clock rate notifier / input clock tracking.

View File

@ -13,12 +13,3 @@ Description:
available for reading data. However, samples can be occasionally skipped
or repeated, depending on the beat between the capture and conversion
rates.
What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistor
Date: December 2015
KernelVersion: 4.4
Contact: linux-iio@vger.kernel.org
Description:
The value of the shunt resistor may be known only at runtime fom an
eeprom content read by a client application. This attribute allows to
set its value in ohms.

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/accel/adi,adxl380.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADXL380/382 3-Axis Digital Accelerometer
maintainers:
- Ramona Gradinariu <ramona.gradinariu@analog.com>
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The ADXL380/ADXL382 is a low noise density, low power, 3-axis
accelerometer with selectable measurement ranges. The ADXL380
supports the ±4 g, ±8 g, and ±16 g ranges, and the ADXL382 supports
±15 g, ±30 g, and ±60 g ranges.
https://www.analog.com/en/products/adxl380.html
properties:
compatible:
enum:
- adi,adxl380
- adi,adxl382
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 2
interrupt-names:
minItems: 1
items:
- enum: [INT0, INT1]
- const: INT1
vddio-supply: true
vsupply-supply: true
required:
- compatible
- reg
- interrupts
- interrupt-names
- vddio-supply
- vsupply-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@54 {
compatible = "adi,adxl380";
reg = <0x54>;
vddio-supply = <&vddio>;
vsupply-supply = <&vsupply>;
interrupt-parent = <&gpio>;
interrupts = <25 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT0";
};
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@0 {
compatible = "adi,adxl380";
reg = <0>;
spi-max-frequency = <8000000>;
vddio-supply = <&vddio>;
vsupply-supply = <&vsupply>;
interrupt-parent = <&gpio>;
interrupts = <25 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT0";
};
};

View File

@ -16,6 +16,7 @@ properties:
- kionix,kxcj91008
- kionix,kxtj21009
- kionix,kxtf9
- kionix,kx022-1020
- kionix,kx023-1025
reg:

View File

@ -0,0 +1,197 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4000.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4000 and similar Analog to Digital Converters
maintainers:
- Marcelo Schmitt <marcelo.schmitt@analog.com>
description: |
Analog Devices AD4000 family of Analog to Digital Converters with SPI support.
Specifications can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4000-4004-4008.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4001-4005.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4002-4006-4010.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4003-4007-4011.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4020-4021-4022.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4001.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4003.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
oneOf:
- const: adi,ad4000
- items:
- enum:
- adi,ad4004
- adi,ad4008
- const: adi,ad4000
- const: adi,ad4001
- items:
- enum:
- adi,ad4005
- const: adi,ad4001
- const: adi,ad4002
- items:
- enum:
- adi,ad4006
- adi,ad4010
- const: adi,ad4002
- const: adi,ad4003
- items:
- enum:
- adi,ad4007
- adi,ad4011
- const: adi,ad4003
- const: adi,ad4020
- items:
- enum:
- adi,ad4021
- adi,ad4022
- const: adi,ad4020
- const: adi,adaq4001
- const: adi,adaq4003
reg:
maxItems: 1
spi-max-frequency:
maximum: 102040816 # for VIO > 2.7 V, 81300813 for VIO > 1.7 V
adi,sdi-pin:
$ref: /schemas/types.yaml#/definitions/string
enum: [ high, low, cs, sdi ]
default: sdi
description:
Describes how the ADC SDI pin is wired. A value of "sdi" indicates that
the ADC SDI is connected to host SDO. "high" indicates that the ADC SDI
pin is hard-wired to logic high (VIO). "low" indicates that it is
hard-wired low (GND). "cs" indicates that the ADC SDI pin is connected to
the host CS line.
'#daisy-chained-devices': true
vdd-supply:
description: A 1.8V supply that powers the chip (VDD).
vio-supply:
description:
A 1.8V to 5.5V supply for the digital inputs and outputs (VIO).
ref-supply:
description:
A 2.5 to 5V supply for the external reference voltage (REF).
cnv-gpios:
description:
When provided, this property indicates the GPIO that is connected to the
CNV pin.
maxItems: 1
adi,high-z-input:
type: boolean
description:
High-Z mode allows the amplifier and RC filter in front of the ADC to be
chosen based on the signal bandwidth of interest, rather than the settling
requirements of the switched capacitor SAR ADC inputs.
adi,gain-milli:
description: |
The hardware gain applied to the ADC input (in milli units).
The gain provided by the ADC input scaler is defined by the hardware
connections between chip pins OUT+, R1K-, R1K1-, R1K+, R1K1+, and OUT-.
If not present, default to 1000 (no actual gain applied).
$ref: /schemas/types.yaml#/definitions/uint16
enum: [454, 909, 1000, 1900]
default: 1000
interrupts:
description:
The SDO pin can also function as a busy indicator. This node should be
connected to an interrupt that is triggered when the SDO line goes low
while the SDI line is high and the CNV line is low ("3-wire" mode) or the
SDI line is low and the CNV line is high ("4-wire" mode); or when the SDO
line goes high while the SDI and CNV lines are high (chain mode),
maxItems: 1
required:
- compatible
- reg
- vdd-supply
- vio-supply
- ref-supply
allOf:
# The configuration register can only be accessed if SDI is connected to MOSI
- if:
required:
- adi,sdi-pin
then:
properties:
adi,high-z-input: false
# chain mode has lower SCLK max rate
- if:
required:
- '#daisy-chained-devices'
then:
properties:
spi-max-frequency:
maximum: 50000000 # for VIO > 2.7 V, 40000000 for VIO > 1.7 V
# Gain property only applies to ADAQ devices
- if:
properties:
compatible:
not:
contains:
enum:
- adi,adaq4001
- adi,adaq4003
then:
properties:
adi,gain-milli: false
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4020";
reg = <0>;
spi-max-frequency = <71000000>;
vdd-supply = <&supply_1_8V>;
vio-supply = <&supply_1_8V>;
ref-supply = <&supply_5V>;
adi,sdi-pin = "cs";
cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>;
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,adaq4003";
reg = <0>;
spi-max-frequency = <80000000>;
vdd-supply = <&supply_1_8V>;
vio-supply = <&supply_1_8V>;
ref-supply = <&supply_5V>;
adi,high-z-input;
adi,gain-milli = /bits/ 16 <454>;
};
};

View File

@ -0,0 +1,254 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4695.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices Easy Drive Multiplexed SAR Analog to Digital Converters
maintainers:
- Michael Hennerich <Michael.Hennerich@analog.com>
- Nuno Sá <nuno.sa@analog.com>
description: |
A family of similar multi-channel analog to digital converters with SPI bus.
* https://www.analog.com/en/products/ad4695.html
* https://www.analog.com/en/products/ad4696.html
* https://www.analog.com/en/products/ad4697.html
* https://www.analog.com/en/products/ad4698.html
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- adi,ad4695
- adi,ad4696
- adi,ad4697
- adi,ad4698
reg:
maxItems: 1
spi-max-frequency:
maximum: 80000000
spi-cpol: true
spi-cpha: true
spi-rx-bus-width:
minimum: 1
maximum: 4
avdd-supply:
description: Analog power supply.
vio-supply:
description: I/O pin power supply.
ldo-in-supply:
description: Internal LDO Input. Mutually exclusive with vdd-supply.
vdd-supply:
description: Core power supply. Mutually exclusive with ldo-in-supply.
ref-supply:
description:
External reference voltage. Mutually exclusive with refin-supply.
refin-supply:
description:
Internal reference buffer input. Mutually exclusive with ref-supply.
com-supply:
description: Common voltage supply for pseudo-differential analog inputs.
adi,no-ref-current-limit:
$ref: /schemas/types.yaml#/definitions/flag
description:
When this flag is present, the REF Overvoltage Reduced Current protection
is disabled.
adi,no-ref-high-z:
$ref: /schemas/types.yaml#/definitions/flag
description:
Enable this flag if the ref-supply requires Reference Input High-Z Mode
to be disabled for proper operation.
cnv-gpios:
description: The Convert Input (CNV). If omitted, CNV is tied to SPI CS.
maxItems: 1
reset-gpios:
description: The Reset Input (RESET). Should be configured GPIO_ACTIVE_LOW.
maxItems: 1
interrupts:
minItems: 1
items:
- description: Signal coming from the BSY_ALT_GP0 pin (ALERT or BUSY).
- description: Signal coming from the GP2 pin (ALERT).
- description: Signal coming from the GP3 pin (BUSY).
interrupt-names:
minItems: 1
items:
- const: gp0
- const: gp2
- const: gp3
gpio-controller: true
"#gpio-cells":
const: 2
description: |
The first cell is the GPn number: 0 to 3.
The second cell takes standard GPIO flags.
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^in(?:[13579]|1[135])-supply$":
description:
Optional voltage supply for odd numbered channels when they are used as
the negative input for a pseudo-differential channel.
"^channel@[0-9a-f]$":
type: object
$ref: adc.yaml
unevaluatedProperties: false
description:
Describes each individual channel. In addition the properties defined
below, bipolar from adc.yaml is also supported.
properties:
reg:
maximum: 15
common-mode-channel:
description:
Describes the common mode channel for single channels. 0xFF is REFGND
and OxFE is COM. Macros are available for these values in
dt-bindings/iio/adi,ad4695.h. Values 1 to 15 correspond to INx inputs.
Only odd numbered INx inputs can be used as common mode channels.
enum: [1, 3, 5, 7, 9, 11, 13, 15, 0xFE, 0xFF]
default: 0xFF
adi,no-high-z:
$ref: /schemas/types.yaml#/definitions/flag
description:
Enable this flag if the input pin requires the Analog Input High-Z
Mode to be disabled for proper operation.
required:
- reg
allOf:
# bipolar mode can't be used with REFGND
- if:
properties:
common-mode-channel:
const: 0xFF
then:
properties:
bipolar: false
required:
- compatible
- reg
- avdd-supply
- vio-supply
allOf:
- oneOf:
- required:
- ldo-in-supply
- required:
- vdd-supply
- oneOf:
- required:
- ref-supply
- required:
- refin-supply
# the internal reference buffer always requires high-z mode
- if:
required:
- refin-supply
then:
properties:
adi,no-ref-high-z: false
# limit channels for 8-channel chips
- if:
properties:
compatible:
contains:
enum:
- adi,ad4697
- adi,ad4698
then:
patternProperties:
"^in(?:9|1[135])-supply$": false
"^channel@[0-7]$":
properties:
reg:
maximum: 7
common-mode-channel:
enum: [1, 3, 5, 7, 0xFE, 0xFF]
"^channel@[8-9a-f]$": false
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/iio/adi,ad4695.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4695";
reg = <0>;
spi-cpol;
spi-cpha;
spi-max-frequency = <80000000>;
avdd-supply = <&power_supply>;
ldo-in-supply = <&power_supply>;
vio-supply = <&io_supply>;
refin-supply = <&supply_5V>;
com-supply = <&supply_2V5>;
in3-supply = <&supply_2V5>;
reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
#address-cells = <1>;
#size-cells = <0>;
/* Pseudo-differential channel between IN0 and REFGND. */
channel@0 {
reg = <0>;
};
/* Pseudo-differential channel between IN1 and COM. */
channel@1 {
reg = <1>;
common-mode-channel = <AD4695_COMMON_MODE_COM>;
bipolar;
};
/* Pseudo-differential channel between IN2 and IN3. */
channel@2 {
reg = <2>;
common-mode-channel = <3>;
bipolar;
};
};
};

View File

@ -39,11 +39,21 @@ properties:
clocks:
maxItems: 1
description: phandle to the master clock (mclk)
description:
Optionally, either a crystal can be attached externally between MCLK1 and
MCLK2 pins, or an external CMOS-compatible clock can drive the MCLK2
pin. If absent, internal 4.92MHz clock is used, which can be made
available on MCLK2 pin.
clock-names:
items:
- const: mclk
enum:
- xtal
- mclk
"#clock-cells":
const: 0
description:
If present when internal clock is used, configured as clock provider.
interrupts:
maxItems: 1
@ -134,8 +144,6 @@ patternProperties:
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- dvdd-supply
- avdd-supply
@ -156,6 +164,18 @@ allOf:
then:
patternProperties:
"^channel@[0-9a-f]+$": false
- if:
anyOf:
- required:
- clocks
- required:
- clock-names
then:
properties:
"#clock-cells": false
required:
- clocks
- clock-names
unevaluatedProperties: false
@ -201,8 +221,7 @@ examples:
spi-max-frequency = <1000000>;
spi-cpol;
spi-cpha;
clocks = <&ad7192_mclk>;
clock-names = "mclk";
#clock-cells = <0>;
interrupts = <25 0x2>;
interrupt-parent = <&gpio>;
aincom-supply = <&aincom>;

View File

@ -15,10 +15,17 @@ description: |
* https://www.analog.com/en/products/ad7381.html
* https://www.analog.com/en/products/ad7383.html
* https://www.analog.com/en/products/ad7384.html
* https://www.analog.com/en/products/ad7386.html
* https://www.analog.com/en/products/ad7387.html
* https://www.analog.com/en/products/ad7388.html
* https://www.analog.com/en/products/ad7380-4.html
* https://www.analog.com/en/products/ad7381-4.html
* https://www.analog.com/en/products/ad7383-4.html
* https://www.analog.com/en/products/ad7384-4.html
* https://www.analog.com/en/products/ad7386-4.html
* https://www.analog.com/en/products/ad7387-4.html
* https://www.analog.com/en/products/ad7388-4.html
$ref: /schemas/spi/spi-peripheral-props.yaml#
@ -29,10 +36,16 @@ properties:
- adi,ad7381
- adi,ad7383
- adi,ad7384
- adi,ad7386
- adi,ad7387
- adi,ad7388
- adi,ad7380-4
- adi,ad7381-4
- adi,ad7383-4
- adi,ad7384-4
- adi,ad7386-4
- adi,ad7387-4
- adi,ad7388-4
reg:
maxItems: 1

View File

@ -35,65 +35,83 @@ properties:
avcc-supply: true
vdrive-supply:
description:
Determines the voltage level at which the interface logic pins will
operate.
refin-supply:
description:
The voltage supply for optional external reference voltage.
interrupts:
description:
The BUSY pin falling edge indicates that the conversion is over, and thus
new data is available.
maxItems: 1
adi,conversion-start-gpios:
description:
Must be the device tree identifier of the CONVST pin.
This logic input is used to initiate conversions on the analog
input channels. As the line is active high, it should be marked
GPIO_ACTIVE_HIGH.
maxItems: 1
Must be the device tree identifier of the CONVST pin(s). This logic input
is used to initiate conversions on the analog input channels. As the line
is active high, it should be marked GPIO_ACTIVE_HIGH.
minItems: 1
maxItems: 2
reset-gpios:
description:
Must be the device tree identifier of the RESET pin. If specified,
it will be asserted during driver probe. As the line is active high,
it should be marked GPIO_ACTIVE_HIGH.
Must be the device tree identifier of the RESET pin. If specified, it will
be asserted during driver probe. On the AD7606x, as the line is active
high, it should be marked GPIO_ACTIVE_HIGH. On the AD7616, as the line is
active low, it should be marked GPIO_ACTIVE_LOW.
maxItems: 1
standby-gpios:
description:
Must be the device tree identifier of the STBY pin. This pin is used
to place the AD7606 into one of two power-down modes, Standby mode or
Must be the device tree identifier of the STBY pin. This pin is used to
place the AD7606 into one of two power-down modes, Standby mode or
Shutdown mode. As the line is active low, it should be marked
GPIO_ACTIVE_LOW.
maxItems: 1
adi,first-data-gpios:
description:
Must be the device tree identifier of the FRSTDATA pin.
The FRSTDATA output indicates when the first channel, V1, is
being read back on either the parallel, byte or serial interface.
As the line is active high, it should be marked GPIO_ACTIVE_HIGH.
Must be the device tree identifier of the FRSTDATA pin. The FRSTDATA
output indicates when the first channel, V1, is being read back on either
the parallel, byte or serial interface. As the line is active high, it
should be marked GPIO_ACTIVE_HIGH.
maxItems: 1
adi,range-gpios:
description:
Must be the device tree identifier of the RANGE pin. The polarity on
this pin determines the input range of the analog input channels. If
this pin is tied to a logic high, the analog input range is ±10V for
all channels. If this pin is tied to a logic low, the analog input range
Must be the device tree identifier of the RANGE pin. The state on this
pin determines the input range of the analog input channels. If this pin
is tied to a logic high, the analog input range is ±10V for all channels.
On the AD760X, if this pin is tied to a logic low, the analog input range
is ±5V for all channels. As the line is active high, it should be marked
GPIO_ACTIVE_HIGH.
maxItems: 1
GPIO_ACTIVE_HIGH. On the AD7616, there are 2 pins, and if the 2 pins are
tied to a logic high, software mode is enabled, otherwise one of the 3
possible range values is selected.
minItems: 1
maxItems: 2
adi,oversampling-ratio-gpios:
description:
Must be the device tree identifier of the over-sampling
mode pins. As the line is active high, it should be marked
GPIO_ACTIVE_HIGH.
Must be the device tree identifier of the over-sampling mode pins. As the
line is active high, it should be marked GPIO_ACTIVE_HIGH. On the AD7606X
parts that support it, if all 3 pins are tied to a logic high, software
mode is enabled.
maxItems: 3
adi,sw-mode:
description:
Software mode of operation, so far available only for ad7616 and ad7606b.
It is enabled when all three oversampling mode pins are connected to
high level. The device is configured by the corresponding registers. If the
adi,oversampling-ratio-gpios property is defined, then the driver will set the
oversampling gpios to high. Otherwise, it is assumed that the pins are hardwired
to VDD.
Software mode of operation, so far available only for AD7616 and AD7606B.
It is enabled when all three oversampling mode pins are connected to high
level for the AD7606B, or both the range selection are connected to high
level for the AD7616. The device is configured by the corresponding
registers. If the adi,oversampling-ratio-gpios property is defined, then
the driver will set the oversampling gpios to high. Otherwise, it is
assumed that the pins are hardwired to VDD.
type: boolean
required:
@ -101,12 +119,57 @@ required:
- reg
- spi-cpha
- avcc-supply
- vdrive-supply
- interrupts
- adi,conversion-start-gpios
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
properties:
compatible:
contains:
const: adi,ad7616
then:
properties:
adi,first-data-gpios: false
standby-gpios: false
adi,range-gpios:
maxItems: 2
else:
properties:
adi,range-gpios:
maxItems: 1
- if:
properties:
compatible:
contains:
enum:
- adi,ad7605-4
- adi,ad7616
then:
properties:
adi,oversampling-ratio-gpios: false
- if:
properties:
compatible:
contains:
enum:
- adi,ad7605-4
- adi,ad7606-4
- adi,ad7606-6
- adi,ad7606-8
then:
properties:
adi,sw-mode: false
else:
properties:
adi,conversion-start-gpios:
maxItems: 1
unevaluatedProperties: false
examples:
@ -125,6 +188,7 @@ examples:
spi-cpha;
avcc-supply = <&adc_vref>;
vdrive-supply = <&vdd_supply>;
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
@ -136,7 +200,6 @@ examples:
<&gpio 23 GPIO_ACTIVE_HIGH>,
<&gpio 26 GPIO_ACTIVE_HIGH>;
standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>;
adi,sw-mode;
};
};
...

View File

@ -28,6 +28,9 @@ properties:
- adi,ad9265
- adi,ad9434
- adi,ad9467
- adi,ad9643
- adi,ad9649
- adi,ad9652
reg:
maxItems: 1

View File

@ -0,0 +1,71 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/microchip,pac1921.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip PAC1921 High-Side Power/Current Monitor with Anaog Output
maintainers:
- Matteo Martelli <matteomartelli3@gmail.com>
description: |
The PAC1921 is a power/current monitoring device with an analog output
and I2C/SMBus interface.
Datasheet can be found here:
https://ww1.microchip.com/downloads/en/DeviceDoc/PAC1921-Data-Sheet-DS20005293E.pdf
properties:
compatible:
const: microchip,pac1921
reg:
maxItems: 1
vdd-supply: true
"#io-channel-cells":
const: 1
shunt-resistor-micro-ohms:
description:
Value in micro Ohms of the shunt resistor connected between
the SENSE+ and SENSE- inputs, across which the current is measured.
Value is needed to compute the scaling of the measured current.
label:
description: Unique name to identify which device this is.
read-integrate-gpios:
description:
READ/INT input pin to control the current state of the device, either in
the INTEGRATE state when driven high, or in the READ state when driven low.
When not connected the pin is floating and it can be overridden by the
INT_EN register bit after asserting the READ/INT_OVR register bit.
maxItems: 1
required:
- compatible
- reg
- vdd-supply
- shunt-resistor-micro-ohms
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
adc@4c {
compatible = "microchip,pac1921";
reg = <0x4c>;
vdd-supply = <&vdd>;
#io-channel-cells = <1>;
label = "vbat";
shunt-resistor-micro-ohms = <10000>;
};
};
...

View File

@ -16,6 +16,9 @@ properties:
- const: rockchip,rk3066-tsadc
- const: rockchip,rk3399-saradc
- const: rockchip,rk3588-saradc
- items:
- const: rockchip,rk3576-saradc
- const: rockchip,rk3588-saradc
- items:
- enum:
- rockchip,px30-saradc

View File

@ -18,18 +18,39 @@ properties:
- sd-modulator
- ads1201
'#io-backend-cells':
const: 0
'#io-channel-cells':
const: 0
vref-supply:
description: Phandle to the vref input analog reference voltage.
dependencies:
vref-supply: [ '#io-backend-cells' ]
required:
- compatible
- '#io-channel-cells'
anyOf:
- required: ['#io-backend-cells']
- required: ['#io-channel-cells']
additionalProperties: false
examples:
- |
ads1202: adc {
// Backend binding example. SD modulator configured as an IIO backend device
ads1201_0: adc {
compatible = "sd-modulator";
vref-supply = <&vdd_adc>;
#io-backend-cells = <0>;
};
- |
// Legacy binding example. SD modulator configured as an IIO channel provider
ads1201_1: adc {
compatible = "sd-modulator";
#io-channel-cells = <0>;
};

View File

@ -54,7 +54,9 @@ properties:
It's not present on stm32f4.
It's required on stm32h7 and stm32mp1.
clock-names: true
clock-names:
minItems: 1
maxItems: 2
st,max-clk-rate-hz:
description:

View File

@ -102,9 +102,11 @@ patternProperties:
items:
minimum: 0
maximum: 7
deprecated: true
st,adc-channel-names:
description: List of single-ended channel names.
deprecated: true
st,filter-order:
description: |
@ -118,6 +120,12 @@ patternProperties:
"#io-channel-cells":
const: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
st,adc-channel-types:
description: |
Single-ended channel input type.
@ -128,6 +136,7 @@ patternProperties:
items:
enum: [ SPI_R, SPI_F, MANCH_R, MANCH_F ]
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
deprecated: true
st,adc-channel-clk-src:
description: |
@ -139,6 +148,7 @@ patternProperties:
items:
enum: [ CLKIN, CLKOUT, CLKOUT_F, CLKOUT_R ]
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
deprecated: true
st,adc-alt-channel:
description:
@ -147,6 +157,7 @@ patternProperties:
If not set, channel n is connected to SPI input n.
If set, channel n is connected to SPI input n + 1.
type: boolean
deprecated: true
st,filter0-sync:
description:
@ -165,11 +176,60 @@ patternProperties:
- compatible
- reg
- interrupts
- st,adc-channels
- st,adc-channel-names
- st,filter-order
- "#io-channel-cells"
patternProperties:
"^channel@[0-7]$":
type: object
$ref: adc.yaml
unevaluatedProperties: false
description: Represents the external channels which are connected to the DFSDM.
properties:
reg:
maximum: 7
label:
description:
Unique name to identify which channel this is.
st,adc-channel-type:
description: |
Single-ended channel input type.
- "SPI_R": SPI with data on rising edge (default)
- "SPI_F": SPI with data on falling edge
- "MANCH_R": manchester codec, rising edge = logic 0, falling edge = logic 1
- "MANCH_F": manchester codec, rising edge = logic 1, falling edge = logic 0
$ref: /schemas/types.yaml#/definitions/string
enum: [ SPI_R, SPI_F, MANCH_R, MANCH_F ]
st,adc-channel-clk-src:
description: |
Conversion clock source.
- "CLKIN": external SPI clock (CLKIN x)
- "CLKOUT": internal SPI clock (CLKOUT) (default)
- "CLKOUT_F": internal SPI clock divided by 2 (falling edge).
- "CLKOUT_R": internal SPI clock divided by 2 (rising edge).
$ref: /schemas/types.yaml#/definitions/string
enum: [ CLKIN, CLKOUT, CLKOUT_F, CLKOUT_R ]
st,adc-alt-channel:
description:
Must be defined if two sigma delta modulators are
connected on same SPI input.
If not set, channel n is connected to SPI input n.
If set, channel n is connected to SPI input n + 1.
type: boolean
io-backends:
description:
Used to pipe external sigma delta modulator or internal ADC backend to DFSDM channel.
maxItems: 1
required:
- reg
allOf:
- if:
properties:
@ -199,9 +259,19 @@ patternProperties:
description:
From common IIO binding. Used to pipe external sigma delta
modulator or internal ADC output to DFSDM channel.
deprecated: true
required:
- io-channels
if:
required:
- st,adc-channels
then:
required:
- io-channels
patternProperties:
"^channel@[0-7]$":
required:
- io-backends
- if:
properties:
@ -298,6 +368,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
// Example 1: Audio use case with generic binding
dfsdm0: filter@0 {
compatible = "st,stm32-dfsdm-dmic";
reg = <0>;
@ -305,12 +376,18 @@ examples:
dmas = <&dmamux1 101 0x400 0x01>;
dma-names = "rx";
#io-channel-cells = <1>;
st,adc-channels = <1>;
st,adc-channel-names = "dmic0";
st,adc-channel-types = "SPI_R";
st,adc-channel-clk-src = "CLKOUT";
#address-cells = <1>;
#size-cells = <0>;
st,filter-order = <5>;
channel@1 {
reg = <1>;
label = "dmic0";
st,adc-channel-type = "SPI_R";
st,adc-channel-clk-src = "CLKOUT";
st,adc-alt-channel;
};
asoc_pdm0: dfsdm-dai {
compatible = "st,stm32h7-dfsdm-dai";
#sound-dai-cells = <0>;
@ -318,19 +395,34 @@ examples:
};
};
dfsdm_pdm1: filter@1 {
// Example 2: Analog use case with generic binding
dfsdm1: filter@1 {
compatible = "st,stm32-dfsdm-adc";
reg = <1>;
interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dmamux1 102 0x400 0x01>;
dma-names = "rx";
#io-channel-cells = <1>;
st,adc-channels = <2 3>;
st,adc-channel-names = "in2", "in3";
st,adc-channel-types = "SPI_R", "SPI_R";
st,adc-channel-clk-src = "CLKOUT_F", "CLKOUT_F";
io-channels = <&sd_adc2 &sd_adc3>;
st,filter-order = <1>;
#io-channel-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
channel@2 {
reg = <2>;
label = "in2";
st,adc-channel-type = "SPI_F";
st,adc-channel-clk-src = "CLKOUT";
st,adc-alt-channel;
io-backends = <&sd_adc2>;
};
channel@3 {
reg = <3>;
label = "in3";
st,adc-channel-type = "SPI_R";
st,adc-channel-clk-src = "CLKOUT";
io-backends = <&sd_adc3>;
};
};
};

View File

@ -0,0 +1,181 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/adi,ltc2664.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices LTC2664 DAC
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
- Kim Seer Paller <kimseer.paller@analog.com>
description: |
Analog Devices LTC2664 4 channel, 12-/16-Bit, +-10V DAC
https://www.analog.com/media/en/technical-documentation/data-sheets/2664fa.pdf
properties:
compatible:
enum:
- adi,ltc2664
reg:
maxItems: 1
spi-max-frequency:
maximum: 50000000
vcc-supply:
description: Analog Supply Voltage Input.
v-pos-supply:
description: Positive Supply Voltage Input.
v-neg-supply:
description: Negative Supply Voltage Input.
iovcc-supply:
description: Digital Input/Output Supply Voltage.
ref-supply:
description:
Reference Input/Output. The voltage at the REF pin sets the full-scale
range of all channels. If not provided the internal reference is used and
also provided on the VREF pin.
reset-gpios:
description:
Active-low Asynchronous Clear Input. A logic low at this level-triggered
input clears the part to the reset code and range determined by the
hardwired option chosen using the MSPAN pins. The control registers are
cleared to zero.
maxItems: 1
adi,manual-span-operation-config:
description:
This property must mimic the MSPAN pin configurations. By tying the MSPAN
pins (MSP2, MSP1 and MSP0) to GND and/or VCC, any output range can be
hardware-configured with different mid-scale or zero-scale reset options.
The hardware configuration is latched during power on reset for proper
operation.
0 - MPS2=GND, MPS1=GND, MSP0=GND (+-10V, reset to 0V)
1 - MPS2=GND, MPS1=GND, MSP0=VCC (+-5V, reset to 0V)
2 - MPS2=GND, MPS1=VCC, MSP0=GND (+-2.5V, reset to 0V)
3 - MPS2=GND, MPS1=VCC, MSP0=VCC (0V to 10, reset to 0V)
4 - MPS2=VCC, MPS1=GND, MSP0=GND (0V to 10V, reset to 5V)
5 - MPS2=VCC, MPS1=GND, MSP0=VCC (0V to 5V, reset to 0V)
6 - MPS2=VCC, MPS1=VCC, MSP0=GND (0V to 5V, reset to 2.5V)
7 - MPS2=VCC, MPS1=VCC, MSP0=VCC (0V to 5V, reset to 0V, enables SoftSpan)
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3, 4, 5, 6, 7]
default: 7
io-channels:
description:
ADC channel to monitor voltages and temperature at the MUXOUT pin.
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^channel@[0-3]$":
$ref: dac.yaml
type: object
additionalProperties: false
properties:
reg:
description: The channel number representing the DAC output channel.
maximum: 3
adi,toggle-mode:
description:
Set the channel as a toggle enabled channel. Toggle operation enables
fast switching of a DAC output between two different DAC codes without
any SPI transaction.
type: boolean
output-range-microvolt:
description:
This property is only allowed when SoftSpan is enabled. If not present,
[0, 5000000] is the default output range.
oneOf:
- items:
- const: 0
- enum: [5000000, 10000000]
- items:
- const: -5000000
- const: 5000000
- items:
- const: -10000000
- const: 10000000
- items:
- const: -2500000
- const: 2500000
required:
- reg
allOf:
- if:
not:
properties:
adi,manual-span-operation-config:
const: 7
then:
patternProperties:
"^channel@[0-3]$":
properties:
output-range-microvolt: false
required:
- compatible
- reg
- spi-max-frequency
- vcc-supply
- iovcc-supply
- v-pos-supply
- v-neg-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
compatible = "adi,ltc2664";
reg = <0>;
spi-max-frequency = <10000000>;
vcc-supply = <&vcc>;
iovcc-supply = <&vcc>;
ref-supply = <&vref>;
v-pos-supply = <&vpos>;
v-neg-supply = <&vneg>;
io-channels = <&adc 0>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
adi,toggle-mode;
output-range-microvolt = <(-10000000) 10000000>;
};
channel@1 {
reg = <1>;
output-range-microvolt= <0 10000000>;
};
};
};
...

View File

@ -0,0 +1,160 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/adi,ltc2672.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices LTC2672 DAC
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
- Kim Seer Paller <kimseer.paller@analog.com>
description: |
Analog Devices LTC2672 5 channel, 12-/16-Bit, 300mA DAC
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2672.pdf
properties:
compatible:
enum:
- adi,ltc2672
reg:
maxItems: 1
spi-max-frequency:
maximum: 50000000
vcc-supply:
description: Analog Supply Voltage Input.
v-neg-supply:
description: Negative Supply Voltage Input.
vdd0-supply:
description: Positive Supply Voltage Input for DAC OUT0.
vdd1-supply:
description: Positive Supply Voltage Input for DAC OUT1.
vdd2-supply:
description: Positive Supply Voltage Input for DAC OUT2.
vdd3-supply:
description: Positive Supply Voltage Input for DAC OUT3.
vdd4-supply:
description: Positive Supply Voltage Input for DAC OUT4.
iovcc-supply:
description: Digital Input/Output Supply Voltage.
ref-supply:
description:
Reference Input/Output. The voltage at the REF pin sets the full-scale
range of all channels. If not provided the internal reference is used and
also provided on the VREF pin.
reset-gpios:
description:
Active Low Asynchronous Clear Input. A logic low at this level triggered
input clears the device to the default reset code and output range, which
is zero-scale with the outputs off. The control registers are cleared to
zero.
maxItems: 1
adi,rfsadj-ohms:
description:
If FSADJ is tied to VCC, an internal RFSADJ (20 kΩ) is selected, which
results in nominal output ranges. When an external resistor of 19 kΩ to
41 kΩ can be used instead by connecting the resistor between FSADJ and GND
it controls the scaling of the ranges, and the internal resistor is
automatically disconnected.
minimum: 19000
maximum: 41000
default: 20000
io-channels:
description:
ADC channel to monitor voltages and currents at the MUX pin.
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^channel@[0-4]$":
$ref: dac.yaml
type: object
additionalProperties: false
properties:
reg:
description: The channel number representing the DAC output channel.
maximum: 4
adi,toggle-mode:
description:
Set the channel as a toggle enabled channel. Toggle operation enables
fast switching of a DAC output between two different DAC codes without
any SPI transaction.
type: boolean
output-range-microamp:
items:
- const: 0
- enum: [3125000, 6250000, 12500000, 25000000, 50000000, 100000000,
200000000, 300000000]
required:
- reg
- output-range-microamp
required:
- compatible
- reg
- spi-max-frequency
- vcc-supply
- iovcc-supply
- v-neg-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
compatible = "adi,ltc2672";
reg = <0>;
spi-max-frequency = <10000000>;
vcc-supply = <&vcc>;
iovcc-supply = <&vcc>;
ref-supply = <&vref>;
v-neg-supply = <&vneg>;
io-channels = <&adc 0>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
adi,toggle-mode;
output-range-microamp = <0 3125000>;
};
channel@1 {
reg = <1>;
output-range-microamp = <0 6250000>;
};
};
};
...

View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/dac.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IIO Common Properties for DAC Channels
maintainers:
- Jonathan Cameron <jic23@kernel.org>
description:
A few properties are defined in a common way for DAC channels.
properties:
$nodename:
pattern: "^channel(@[0-9a-f]+)?$"
description:
A channel index should match reg.
reg:
maxItems: 1
label:
description: Unique name to identify which channel this is.
output-range-microamp:
maxItems: 2
minItems: 2
description:
Specify the channel output full scale range in microamperes.
output-range-microvolt:
maxItems: 2
minItems: 2
description:
Specify the channel output full scale range in microvolts.
anyOf:
- oneOf:
- required:
- reg
- output-range-microamp
- required:
- reg
- output-range-microvolt
- required:
- reg
additionalProperties: true

View File

@ -17,6 +17,7 @@ description: |
applications.
https://www.analog.com/en/products/adf4377.html
https://www.analog.com/en/products/adf4378.html
properties:
compatible:
@ -73,6 +74,15 @@ required:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
properties:
compatible:
contains:
enum:
- adi,adf4378
then:
properties:
clk2-enable-gpios: false
unevaluatedProperties: false

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/humidity/sciosense,ens210.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ScioSense ENS210 temperature and humidity sensor
maintainers:
- Joshua Felmeden <jfelmeden@thegoodpenguin.co.uk>
description: |
Temperature and Humidity sensor.
Datasheet:
https://www.sciosense.com/wp-content/uploads/2024/04/ENS21x-Datasheet.pdf
https://www.sciosense.com/wp-content/uploads/2023/12/ENS210-Datasheet.pdf
properties:
compatible:
oneOf:
- items:
- enum:
- sciosense,ens210a
- sciosense,ens211
- sciosense,ens212
- sciosense,ens213a
- sciosense,ens215
- const: sciosense,ens210
- const: sciosense,ens210
reg:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@43 {
compatible = "sciosense,ens210";
reg = <0x43>;
};
};
...

View File

@ -14,7 +14,9 @@ description:
properties:
compatible:
const: liteon,ltrf216a
enum:
- liteon,ltr308
- liteon,ltrf216a
reg:
maxItems: 1

View File

@ -0,0 +1,53 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bh1745.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BH1745 colour sensor
maintainers:
- Mudit Sharma <muditsharma.info@gmail.com>
description:
BH1745 is an I2C colour sensor with red, green, blue and clear
channels. It has a programmable active low interrupt pin.
Interrupt occurs when the signal from the selected interrupt
source channel crosses set interrupt threshold high/low level.
properties:
compatible:
const: rohm,bh1745
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
colour-sensor@38 {
compatible = "rohm,bh1745";
reg = <0x38>;
interrupt-parent = <&gpio>;
interrupts = <19 IRQ_TYPE_LEVEL_LOW>;
vdd-supply = <&vdd>;
};
};
...

View File

@ -1,23 +1,22 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bu27034.yaml#
$id: http://devicetree.org/schemas/iio/light/rohm,bu27034anuc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BU27034 ambient light sensor
title: ROHM BU27034ANUC ambient light sensor
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes
ROHM BU27034ANUC is an ambient light sensor with 2 channels and 2 photo diodes
capable of detecting a very wide range of illuminance. Typical application
is adjusting LCD and backlight power of TVs and mobile phones.
https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf
properties:
compatible:
const: rohm,bu27034
const: rohm,bu27034anuc
reg:
maxItems: 1
@ -37,7 +36,7 @@ examples:
#size-cells = <0>;
light-sensor@38 {
compatible = "rohm,bu27034";
compatible = "rohm,bu27034anuc";
reg = <0x38>;
vdd-supply = <&vdd>;
};

View File

@ -18,10 +18,15 @@ allOf:
properties:
compatible:
enum:
- sensortek,stk3310
- sensortek,stk3311
- sensortek,stk3335
oneOf:
- enum:
- sensortek,stk3310
- sensortek,stk3311
- sensortek,stk3335
- items:
- enum:
- sensortek,stk3013
- const: sensortek,stk3310
reg:
maxItems: 1

View File

@ -23,7 +23,6 @@ properties:
- ak8963
- ak09911
- ak09912
- ak09916
deprecated: true
reg:

View File

@ -36,6 +36,9 @@ properties:
interrupts:
maxItems: 1
mount-matrix:
description: an optional 3x3 mounting rotation matrix.
additionalProperties: false
required:

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/pressure/sensirion,sdp500.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: sdp500/sdp510 pressure sensor with I2C bus interface
maintainers:
- Petar Stoykov <petar.stoykov@prodrive-technologies.com>
description: |
Pressure sensor from Sensirion with I2C bus interface.
There is no software difference between sdp500 and sdp510.
properties:
compatible:
oneOf:
- items:
- const: sensirion,sdp510
- const: sensirion,sdp500
- const: sensirion,sdp500
reg:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pressure@40 {
compatible = "sensirion,sdp500";
reg = <0x40>;
vdd-supply = <&foo>;
};
};

View File

@ -0,0 +1,93 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/proximity/tyhx,hx9023s.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TYHX HX9023S capacitive proximity sensor
maintainers:
- Yasin Lee <yasin.lee.x@gmail.com>
description: |
TYHX HX9023S proximity sensor. Datasheet can be found here:
http://www.tianyihexin.com/ueditor/php/upload/file/20240614/1718336303992081.pdf
properties:
compatible:
const: tyhx,hx9023s
reg:
maxItems: 1
interrupts:
description:
Generated by device to announce preceding read request has finished
and data is available or that a close/far proximity event has happened.
maxItems: 1
vdd-supply: true
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^channel@[0-4]$":
$ref: /schemas/iio/adc/adc.yaml
type: object
unevaluatedProperties: false
properties:
reg:
minimum: 0
maximum: 4
description: The channel number.
required:
- compatible
- reg
- vdd-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
proximity@2a {
compatible = "tyhx,hx9023s";
reg = <0x2a>;
interrupt-parent = <&pio>;
interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&pp1800_prox>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
single-channel = <0>;
};
channel@1 {
reg = <1>;
single-channel = <1>;
};
channel@2 {
reg = <2>;
single-channel = <2>;
};
channel@3 {
reg = <3>;
diff-channels = <1 0>;
};
channel@4 {
reg = <4>;
diff-channels = <2 0>;
};
};
};

View File

@ -1535,6 +1535,8 @@ patternProperties:
description: Turing Machines, Inc.
"^tyan,.*":
description: Tyan Computer Corporation
"^tyhx,.*":
description: NanjingTianyihexin Electronics Ltd.
"^u-blox,.*":
description: u-blox
"^u-boot,.*":

View File

@ -0,0 +1,131 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
AD4000 driver
=============
Device driver for Analog Devices Inc. AD4000 series of ADCs.
Supported devices
=================
* `AD4000 <https://www.analog.com/AD4000>`_
* `AD4001 <https://www.analog.com/AD4001>`_
* `AD4002 <https://www.analog.com/AD4002>`_
* `AD4003 <https://www.analog.com/AD4003>`_
* `AD4004 <https://www.analog.com/AD4004>`_
* `AD4005 <https://www.analog.com/AD4005>`_
* `AD4006 <https://www.analog.com/AD4006>`_
* `AD4007 <https://www.analog.com/AD4007>`_
* `AD4008 <https://www.analog.com/AD4008>`_
* `AD4010 <https://www.analog.com/AD4010>`_
* `AD4011 <https://www.analog.com/AD4011>`_
* `AD4020 <https://www.analog.com/AD4020>`_
* `AD4021 <https://www.analog.com/AD4021>`_
* `AD4022 <https://www.analog.com/AD4022>`_
* `ADAQ4001 <https://www.analog.com/ADAQ4001>`_
* `ADAQ4003 <https://www.analog.com/ADAQ4003>`_
Wiring connections
------------------
Devices of the AD4000 series can be connected to the SPI host controller in a
few different modes.
CS mode, 3-wire turbo mode
^^^^^^^^^^^^^^^^^^^^^^^^^^
Datasheet "3-wire" mode is what most resembles standard SPI connection which,
for these devices, comprises of connecting the controller CS line to device CNV
pin and other SPI lines as usual. This configuration is (misleadingly) called
"CS Mode, 3-Wire Turbo Mode" connection in datasheets.
NOTE: The datasheet definition of 3-wire mode for the AD4000 series is NOT the
same of standard spi-3wire mode.
This is the only connection mode that allows configuration register access but
it requires the SPI controller to support the ``SPI_MOSI_IDLE_HIGH`` feature.
Omit the ``adi,sdi-pin`` property in device tree to select this mode.
::
+-------------+
+ ----------------------------------| SDO |
| | |
| +-------------------| CS |
| v | |
| +--------------------+ | HOST |
| | CNV | | |
+--->| SDI AD4000 SDO |-------->| SDI |
| SCK | | |
+--------------------+ | |
^ | |
+--------------------| SCLK |
+-------------+
CS mode, 3-wire, without busy indicator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another wiring configuration supported as "3-wire" mode has the SDI pin
hard-wired to digital input/output interface supply (VIO). In this setup, the
controller is not required to support ``SPI_MOSI_IDLE_HIGH`` but register access
is not possible. This connection mode saves one wire and works with any SPI
controller.
Set the ``adi,sdi-pin`` device tree property to ``"high"`` to select this mode.
::
+-------------+
+--------------------| CS |
v | |
VIO +--------------------+ | HOST |
| | CNV | | |
+--->| SDI AD4000 SDO |-------->| SDI |
| SCK | | |
+--------------------+ | |
^ | |
+--------------------| SCLK |
+-------------+
Alternatively, a GPIO may be connected to the device CNV pin. This is similar to
the previous wiring configuration but saves the use of a CS line.
::
+-------------+
+--------------------| GPIO |
v | |
VIO +--------------------+ | HOST |
| | CNV | | |
+--->| SDI AD4000 SDO |-------->| SDI |
| SCK | | |
+--------------------+ | |
^ | |
+--------------------| SCLK |
+-------------+
CS mode, 4-wire without busy indicator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In datasheet "4-wire" mode, the controller CS line is connected to the ADC SDI
pin and a GPIO is connected to the ADC CNV pin. This connection mode may better
suit scenarios where multiple ADCs can share one CNV trigger.
Set ``adi,sdi-pin`` to ``"cs"`` to select this mode.
::
+-------------+
+ ----------------------------------| CS |
| | |
| +-------------------| GPIO |
| v | |
| +--------------------+ | HOST |
| | CNV | | |
+--->| SDI AD4000 SDO |-------->| SDI |
| SCK | | |
+--------------------+ | |
^ | |
+--------------------| SCLK |
+-------------+

View File

@ -0,0 +1,162 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
AD4695 driver
=============
ADC driver for Analog Devices Inc. AD4695 and similar devices. The module name
is ``ad4695``.
Supported devices
=================
The following chips are supported by this driver:
* `AD4695 <https://www.analog.com/AD4695>`_
* `AD4696 <https://www.analog.com/AD4696>`_
* `AD4697 <https://www.analog.com/AD4697>`_
* `AD4698 <https://www.analog.com/AD4698>`_
Supported features
==================
SPI wiring modes
----------------
The driver currently supports the following SPI wiring configuration:
4-wire mode
^^^^^^^^^^^
In this mode, CNV and CS are tied together and there is a single SDO line.
.. code-block::
+-------------+ +-------------+
| CS |<-+------| CS |
| CNV |<-+ | |
| ADC | | HOST |
| | | |
| SDI |<--------| SDO |
| SDO |-------->| SDI |
| SCLK |<--------| SCLK |
+-------------+ +-------------+
To use this mode, in the device tree, omit the ``cnv-gpios`` and
``spi-rx-bus-width`` properties.
Channel configuration
---------------------
Since the chip supports multiple ways to configure each channel, this must be
described in the device tree based on what is actually wired up to the inputs.
There are three typical configurations:
An ``INx`` pin is used as the positive input with the ``REFGND``, ``COM`` or
the next ``INx`` pin as the negative input.
Pairing with REFGND
^^^^^^^^^^^^^^^^^^^
Each ``INx`` pin can be used as a pseudo-differential input in conjunction with
the ``REFGND`` pin. The device tree will look like this:
.. code-block::
channel@0 {
reg = <0>; /* IN0 */
};
If no other channel properties are needed (e.g. ``adi,no-high-z``), the channel
node can be omitted entirely.
This will appear on the IIO bus as the ``voltage0`` channel. The processed value
(*raw × scale*) will be the voltage present on the ``IN0`` pin relative to
``REFGND``. (Offset is always 0 when pairing with ``REFGND``.)
Pairing with COM
^^^^^^^^^^^^^^^^
Each ``INx`` pin can be used as a pseudo-differential input in conjunction with
the ``COM`` pin. The device tree will look like this:
.. code-block::
com-supply = <&vref_div_2>;
channel@1 {
reg = <1>; /* IN1 */
common-mode-channel = <AD4695_COMMON_MODE_COM>;
bipolar;
};
This will appear on the IIO bus as the ``voltage1`` channel. The processed value
(*(raw + offset) × scale*) will be the voltage measured on the ``IN1`` pin
relative to ``REFGND``. (The offset is determined by the ``com-supply`` voltage.)
The macro comes from:
.. code-block::
#include <dt-bindings/iio/adi,ad4695.h>
Pairing two INx pins
^^^^^^^^^^^^^^^^^^^^
An even-numbered ``INx`` pin and the following odd-numbered ``INx`` pin can be
used as a pseudo-differential input. The device tree for using ``IN2`` as the
positive input and ``IN3`` as the negative input will look like this:
.. code-block::
in3-supply = <&vref_div_2>;
channel@2 {
reg = <2>; /* IN2 */
common-mode-channel = <3>; /* IN3 */
bipolar;
};
This will appear on the IIO bus as the ``voltage2`` channel. The processed value
(*(raw + offset) × scale*) will be the voltage measured on the ``IN1`` pin
relative to ``REFGND``. (Offset is determined by the ``in3-supply`` voltage.)
VCC supply
----------
The chip supports being powered by an external LDO via the ``VCC`` input or an
internal LDO via the ``LDO_IN`` input. The driver looks at the device tree to
determine which is being used. If ``ldo-supply`` is present, then the internal
LDO is used. If ``vcc-supply`` is present, then the external LDO is used and
the internal LDO is disabled.
Reference voltage
-----------------
The chip supports an external reference voltage via the ``REF`` input or an
internal buffered reference voltage via the ``REFIN`` input. The driver looks
at the device tree to determine which is being used. If ``ref-supply`` is
present, then the external reference voltage is used and the internal buffer is
disabled. If ``refin-supply`` is present, then the internal buffered reference
voltage is used.
Unimplemented features
----------------------
- Additional wiring modes
- Threshold events
- Oversampling
- Gain/offset calibration
- GPIO support
- CRC support
Device buffers
==============
This driver supports hardware triggered buffers. This uses the "advanced
sequencer" feature of the chip to trigger a burst of conversions.
Also see :doc:`iio_devbuf` for more general information.

View File

@ -0,0 +1,130 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
AD7380 driver
=============
ADC driver for Analog Devices Inc. AD7380 and similar devices. The module name
is ``ad7380``.
Supported devices
=================
The following chips are supported by this driver:
* `AD7380 <https://www.analog.com/en/products/ad7380.html>`_
* `AD7381 <https://www.analog.com/en/products/ad7381.html>`_
* `AD7383 <https://www.analog.com/en/products/ad7383.html>`_
* `AD7384 <https://www.analog.com/en/products/ad7384.html>`_
* `AD7386 <https://www.analog.com/en/products/ad7386.html>`_
* `AD7387 <https://www.analog.com/en/products/ad7387.html>`_
* `AD7388 <https://www.analog.com/en/products/ad7388.html>`_
* `AD7380-4 <https://www.analog.com/en/products/ad7380-4.html>`_
* `AD7381-4 <https://www.analog.com/en/products/ad7381-4.html>`_
* `AD7383-4 <https://www.analog.com/en/products/ad7383-4.html>`_
* `AD7384-4 <https://www.analog.com/en/products/ad7384-4.html>`_
* `AD7386-4 <https://www.analog.com/en/products/ad7386-4.html>`_
* `AD7387-4 <https://www.analog.com/en/products/ad7387-4.html>`_
* `AD7388-4 <https://www.analog.com/en/products/ad7388-4.html>`_
Supported features
==================
SPI wiring modes
----------------
ad738x ADCs can output data on several SDO lines (1/2/4). The driver currently
supports only 1 SDO line.
Reference voltage
-----------------
2 possible reference voltage sources are supported:
- Internal reference (2.5V)
- External reference (2.5V to 3.3V)
The source is determined by the device tree. If ``refio-supply`` is present,
then the external reference is used, else the internal reference is used.
Oversampling and resolution boost
---------------------------------
This family supports 2 types of oversampling: normal average and rolling
average. Only normal average is supported by the driver, as rolling average can
be achieved by processing a captured data buffer. The following ratios are
available: 1 (oversampling disabled)/2/4/8/16/32.
When the on-chip oversampling function is enabled the performance of the ADC can
exceed the default resolution. To accommodate the performance boost achievable,
it is possible to enable an additional two bits of resolution. Because the
resolution boost feature can only be enabled when oversampling is enabled and
oversampling is not as useful without the resolution boost, the driver
automatically enables the resolution boost if and only if oversampling is
enabled.
Since the resolution boost feature causes 16-bit chips to now have 18-bit data
which means the storagebits has to change from 16 to 32 bits, we use the new
ext_scan_type feature to allow changing the scan_type at runtime. Unfortunately
libiio does not support it. So when enabling or disabling oversampling, user
must restart iiod using the following command:
.. code-block:: bash
root:~# systemctl restart iiod
Channel selection and sequencer (single-end chips only)
-------------------------------------------------------
Single-ended chips of this family (ad7386/7/8(-4)) have a 2:1 multiplexer in
front of each ADC. They also include additional configuration registers that
allow for either manual selection or automatic switching (sequencer mode), of
the multiplexer inputs.
From an IIO point of view, all inputs are exported, i.e ad7386/7/8
export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
Inputs ``AinX0`` of multiplexers correspond to the first half of IIO channels (i.e
0-1 or 0-3) and inputs ``AinX1`` correspond to second half (i.e 2-3 or 4-7).
Example for AD7386/7/8 (2 channels parts):
.. code-block::
IIO | AD7386/7/8
| +----------------------------
| | _____ ______
| | | | | |
voltage0 | AinA0 --|--->| | | |
| | | mux |----->| ADCA |---
voltage2 | AinA1 --|--->| | | |
| | |_____| |_____ |
| | _____ ______
| | | | | |
voltage1 | AinB0 --|--->| | | |
| | | mux |----->| ADCB |---
voltage3 | AinB1 --|--->| | | |
| | |_____| |______|
| |
| +----------------------------
When enabling sequencer mode, the effective sampling rate is divided by two.
Unimplemented features
----------------------
- 2/4 SDO lines
- Rolling average oversampling
- Power down mode
- CRC indication
- Alert
Device buffers
==============
This driver supports IIO triggered buffers.
See :doc:`iio_devbuf` for more information.

View File

@ -0,0 +1,233 @@
.. SPDX-License-Identifier: GPL-2.0
===============
ADXL380 driver
===============
This driver supports Analog Device's ADXL380/382 on SPI/I2C bus.
1. Supported devices
====================
* `ADXL380 <https://www.analog.com/ADXL380>`_
* `ADXL382 <https://www.analog.com/ADXL382>`_
The ADXL380/ADXL382 is a low noise density, low power, 3-axis accelerometer with
selectable measurement ranges. The ADXL380 supports the ±4 g, ±8 g, and ±16 g
ranges, and the ADXL382 supports ±15 g, ±30 g, and ±60 g ranges.
2. Device attributes
====================
Accelerometer measurements are always provided.
Temperature data are also provided. This data can be used to monitor the
internal system temperature or to improve the temperature stability of the
device via calibration.
Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
where X is the IIO index of the device. Under these folders reside a set of
device files, depending on the characteristics and features of the hardware
device in questions. These files are consistently generalized and documented in
the IIO ABI documentation.
The following tables show the adxl380 related device files, found in the
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+---------------------------------------------------+----------------------------------------------------------+
| 3-Axis Accelerometer related device files | Description |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_scale | Scale for the accelerometer channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_filter_high_pass_3db_frequency | Low pass filter bandwidth. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_filter_high_pass_3db_frequency_available | Available low pass filter bandwidth configurations. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_filter_low_pass_3db_frequency | High pass filter bandwidth. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_filter_low_pass_3db_frequency_available | Available high pass filter bandwidth configurations. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_y_calibbias | y-axis acceleration offset correction |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
+---------------------------------------------------+----------------------------------------------------------+
+----------------------------------+--------------------------------------------+
| Temperature sensor related files | Description |
+----------------------------------+--------------------------------------------+
| in_temp_raw | Raw temperature channel value. |
+----------------------------------+--------------------------------------------+
| in_temp_offset | Offset for the temperature sensor channel. |
+----------------------------------+--------------------------------------------+
| in_temp_scale | Scale for the temperature sensor channel. |
+----------------------------------+--------------------------------------------+
+------------------------------+----------------------------------------------+
| Miscellaneous device files | Description |
+------------------------------+----------------------------------------------+
| name | Name of the IIO device. |
+------------------------------+----------------------------------------------+
| sampling_frequency | Currently selected sample rate. |
+------------------------------+----------------------------------------------+
| sampling_frequency_available | Available sampling frequency configurations. |
+------------------------------+----------------------------------------------+
Channels processed values
-------------------------
A channel value can be read from its _raw attribute. The value returned is the
raw value as reported by the devices. To get the processed value of the channel,
apply the following formula:
.. code-block:: bash
processed value = (_raw + _offset) * _scale
Where _offset and _scale are device attributes. If no _offset attribute is
present, simply assume its value is 0.
The adis16475 driver offers data for 2 types of channels, the table below shows
the measurement units for the processed value, which are defined by the IIO
framework:
+-------------------------------------+---------------------------+
| Channel type | Measurement unit |
+-------------------------------------+---------------------------+
| Acceleration on X, Y, and Z axis | Meters per Second squared |
+-------------------------------------+---------------------------+
| Temperature | Millidegrees Celsius |
+-------------------------------------+---------------------------+
Usage examples
--------------
Show device name:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat name
adxl382
Show accelerometer channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
-1771
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
282
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
-1523
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
0.004903325
- X-axis acceleration = in_accel_x_raw * in_accel_scale = 8.683788575 m/s^2
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = 1.38273765 m/s^2
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = -7.467763975 m/s^2
Set calibration offset for accelerometer channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
0
root:/sys/bus/iio/devices/iio:device0> echo 50 > in_accel_x_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
50
Set sampling frequency:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency
16000
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency_available
16000 32000 64000
root:/sys/bus/iio/devices/iio:device0> echo 32000 > sampling_frequency
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency
32000
Set low pass filter bandwidth for accelerometer channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency
32000
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency_available
32000 8000 4000 2000
root:/sys/bus/iio/devices/iio:device0> echo 2000 > in_accel_filter_low_pass_3db_frequency
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency
2000
3. Device buffers
=================
This driver supports IIO buffers.
All devices support retrieving the raw acceleration and temperature measurements
using buffers.
Usage examples
--------------
Select channels for buffer read:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp_en
Set the number of samples to be stored in the buffer:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
Enable buffer readings:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
Obtain buffered data:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
...
002bc300 f7 e7 00 a8 fb c5 24 80 f7 e7 01 04 fb d6 24 80 |......$.......$.|
002bc310 f7 f9 00 ab fb dc 24 80 f7 c3 00 b8 fb e2 24 80 |......$.......$.|
002bc320 f7 fb 00 bb fb d1 24 80 f7 b1 00 5f fb d1 24 80 |......$...._..$.|
002bc330 f7 c4 00 c6 fb a6 24 80 f7 a6 00 68 fb f1 24 80 |......$....h..$.|
002bc340 f7 b8 00 a3 fb e7 24 80 f7 9a 00 b1 fb af 24 80 |......$.......$.|
002bc350 f7 b1 00 67 fb ee 24 80 f7 96 00 be fb 92 24 80 |...g..$.......$.|
002bc360 f7 ab 00 7a fc 1b 24 80 f7 b6 00 ae fb 76 24 80 |...z..$......v$.|
002bc370 f7 ce 00 a3 fc 02 24 80 f7 c0 00 be fb 8b 24 80 |......$.......$.|
002bc380 f7 c3 00 93 fb d0 24 80 f7 ce 00 d8 fb c8 24 80 |......$.......$.|
002bc390 f7 bd 00 c0 fb 82 24 80 f8 00 00 e8 fb db 24 80 |......$.......$.|
002bc3a0 f7 d8 00 d3 fb b4 24 80 f8 0b 00 e5 fb c3 24 80 |......$.......$.|
002bc3b0 f7 eb 00 c8 fb 92 24 80 f7 e7 00 ea fb cb 24 80 |......$.......$.|
002bc3c0 f7 fd 00 cb fb 94 24 80 f7 e3 00 f2 fb b8 24 80 |......$.......$.|
...
See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered
data is structured.
4. IIO Interfacing Tools
========================
See ``Documentation/iio/iio_tools.rst`` for the description of the available IIO
interfacing tools.

View File

@ -18,8 +18,12 @@ Industrial I/O Kernel Drivers
.. toctree::
:maxdepth: 1
ad4000
ad4695
ad7380
ad7944
adis16475
adis16480
adxl380
bno055
ep93xx_adc

View File

@ -614,6 +614,89 @@ queue, and then start some asynchronous transfer engine (unless it's
already running).
Extensions to the SPI protocol
------------------------------
The fact that SPI doesn't have a formal specification or standard permits chip
manufacturers to implement the SPI protocol in slightly different ways. In most
cases, SPI protocol implementations from different vendors are compatible among
each other. For example, in SPI mode 0 (CPOL=0, CPHA=0) the bus lines may behave
like the following:
::
nCSx ___ ___
\_________________________________________________________________/
• •
• •
SCLK ___ ___ ___ ___ ___ ___ ___ ___
_______/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \_____
• : ; : ; : ; : ; : ; : ; : ; : ; •
• : ; : ; : ; : ; : ; : ; : ; : ; •
MOSI XXX__________ _______ _______ ________XXX
0xA5 XXX__/ 1 \_0_____/ 1 \_0_______0_____/ 1 \_0_____/ 1 \_XXX
• ; ; ; ; ; ; ; ; •
• ; ; ; ; ; ; ; ; •
MISO XXX__________ _______________________ _______ XXX
0xBA XXX__/ 1 \_____0_/ 1 1 1 \_____0__/ 1 \____0__XXX
Legend::
• marks the start/end of transmission;
: marks when data is clocked into the peripheral;
; marks when data is clocked into the controller;
X marks when line states are not specified.
In some few cases, chips extend the SPI protocol by specifying line behaviors
that other SPI protocols don't (e.g. data line state for when CS is not
asserted). Those distinct SPI protocols, modes, and configurations are supported
by different SPI mode flags.
MOSI idle state configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Common SPI protocol implementations don't specify any state or behavior for the
MOSI line when the controller is not clocking out data. However, there do exist
peripherals that require specific MOSI line state when data is not being clocked
out. For example, if the peripheral expects the MOSI line to be high when the
controller is not clocking out data (``SPI_MOSI_IDLE_HIGH``), then a transfer in
SPI mode 0 would look like the following:
::
nCSx ___ ___
\_________________________________________________________________/
• •
• •
SCLK ___ ___ ___ ___ ___ ___ ___ ___
_______/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \_____
• : ; : ; : ; : ; : ; : ; : ; : ; •
• : ; : ; : ; : ; : ; : ; : ; : ; •
MOSI _____ _______ _______ _______________ ___
0x56 \_0_____/ 1 \_0_____/ 1 \_0_____/ 1 1 \_0_____/
• ; ; ; ; ; ; ; ; •
• ; ; ; ; ; ; ; ; •
MISO XXX__________ _______________________ _______ XXX
0xBA XXX__/ 1 \_____0_/ 1 1 1 \_____0__/ 1 \____0__XXX
Legend::
• marks the start/end of transmission;
: marks when data is clocked into the peripheral;
; marks when data is clocked into the controller;
X marks when line states are not specified.
In this extension to the usual SPI protocol, the MOSI line state is specified to
be kept high when CS is asserted but the controller is not clocking out data to
the peripheral and also when CS is not asserted.
Peripherals that require this extension must request it by setting the
``SPI_MOSI_IDLE_HIGH`` bit into the mode attribute of their ``struct
spi_device`` and call spi_setup(). Controllers that support this extension
should indicate it by setting ``SPI_MOSI_IDLE_HIGH`` in the mode_bits attribute
of their ``struct spi_controller``. The configuration to idle MOSI low is
analogous but uses the ``SPI_MOSI_IDLE_LOW`` mode bit.
THANKS TO
---------
Contributors to Linux-SPI discussions include (in alphabetical order,

View File

@ -448,6 +448,7 @@ S: Supported
W: https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad738x
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
F: Documentation/iio/ad7380.rst
F: drivers/iio/adc/ad7380.c
AD7877 TOUCHSCREEN DRIVER
@ -619,6 +620,17 @@ F: drivers/iio/accel/adxl372.c
F: drivers/iio/accel/adxl372_i2c.c
F: drivers/iio/accel/adxl372_spi.c
ADXL380 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
M: Ramona Gradinariu <ramona.gradinariu@analog.com>
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/accel/adi,adxl380.yaml
F: drivers/iio/accel/adxl380.c
F: drivers/iio/accel/adxl380.h
F: drivers/iio/accel/adxl380_i2c.c
F: drivers/iio/accel/adxl380_spi.c
AF8133J THREE-AXIS MAGNETOMETER DRIVER
M: Ondřej Jirman <megi@xff.cz>
S: Maintained
@ -1202,6 +1214,15 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml
F: drivers/iio/dac/ad3552r.c
ANALOG DEVICES INC AD4000 DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml
F: Documentation/iio/ad4000.rst
F: drivers/iio/adc/ad4000.c
ANALOG DEVICES INC AD4130 DRIVER
M: Cosmin Tanislav <cosmin.tanislav@analog.com>
L: linux-iio@vger.kernel.org
@ -1211,6 +1232,18 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
F: drivers/iio/adc/ad4130.c
ANALOG DEVICES INC AD4695 DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
M: Nuno Sá <nuno.sa@analog.com>
R: David Lechner <dlechner@baylibre.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml
F: Documentation/iio/ad4695.rst
F: drivers/iio/adc/ad4695.c
F: include/dt-bindings/iio/adi,ad4695.h
ANALOG DEVICES INC AD7091R DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
L: linux-iio@vger.kernel.org
@ -1277,6 +1310,16 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
F: drivers/iio/adc/ad7780.c
ANALOG DEVICES INC AD9467 DRIVER
M: Michael Hennerich <Michael.Hennerich@analog.com>
M: Nuno Sa <nuno.sa@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/ABI/testing/debugfs-iio-ad9467
F: Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
F: drivers/iio/adc/ad9467.c
ANALOG DEVICES INC AD9739a DRIVER
M: Nuno Sa <nuno.sa@analog.com>
M: Dragos Bogdan <dragos.bogdan@analog.com>
@ -10861,6 +10904,7 @@ M: Nuno Sa <nuno.sa@analog.com>
R: Olivier Moysan <olivier.moysan@foss.st.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/debugfs-iio-backend
F: drivers/iio/industrialio-backend.c
F: include/linux/iio/backend.h
@ -13261,6 +13305,16 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
F: drivers/iio/dac/ltc1660.c
LTC2664 IIO DAC DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
M: Kim Seer Paller <kimseer.paller@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,ltc2664.yaml
F: Documentation/devicetree/bindings/iio/dac/adi,ltc2672.yaml
F: drivers/iio/dac/ltc2664.c
LTC2688 IIO DAC DRIVER
M: Nuno Sá <nuno.sa@analog.com>
L: linux-iio@vger.kernel.org
@ -15022,6 +15076,13 @@ F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml
F: drivers/nvmem/microchip-otpc.c
F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h
MICROCHIP PAC1921 POWER/CURRENT MONITOR DRIVER
M: Matteo Martelli <matteomartelli3@gmail.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/adc/microchip,pac1921.yaml
F: drivers/iio/adc/pac1921.c
MICROCHIP PAC1934 POWER/ENERGY MONITOR DRIVER
M: Marius Cristea <marius.cristea@microchip.com>
L: linux-iio@vger.kernel.org
@ -19767,6 +19828,12 @@ S: Supported
F: drivers/power/supply/bd99954-charger.c
F: drivers/power/supply/bd99954-charger.h
ROHM BH1745 COLOUR SENSOR
M: Mudit Sharma <muditsharma.info@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/light/bh1745.c
ROHM BH1750 AMBIENT LIGHT SENSOR DRIVER
M: Tomasz Duszynski <tduszyns@gmail.com>
S: Maintained
@ -20582,6 +20649,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml
F: drivers/iio/chemical/scd4x.c
SENSIRION SDP500 DIFFERENTIAL PRESSURE SENSOR DRIVER
M: Petar Stoykov <petar.stoykov@prodrive-technologies.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/pressure/sensirion,sdp500.yaml
F: drivers/iio/pressure/sdp500.c
SENSIRION SGP40 GAS SENSOR DRIVER
M: Andreas Klinger <ak@it-klinger.de>
S: Maintained

View File

@ -177,6 +177,33 @@ config ADXL372_I2C
To compile this driver as a module, choose M here: the
module will be called adxl372_i2c.
config ADXL380
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config ADXL380_SPI
tristate "Analog Devices ADXL380 3-Axis Accelerometer SPI Driver"
depends on SPI
select ADXL380
select REGMAP_SPI
help
Say yes here to add support for the Analog Devices ADXL380 triaxial
acceleration sensor.
To compile this driver as a module, choose M here: the
module will be called adxl380_spi.
config ADXL380_I2C
tristate "Analog Devices ADXL380 3-Axis Accelerometer I2C Driver"
depends on I2C
select ADXL380
select REGMAP_I2C
help
Say yes here to add support for the Analog Devices ADXL380 triaxial
acceleration sensor.
To compile this driver as a module, choose M here: the
module will be called adxl380_i2c.
config BMA180
tristate "Bosch BMA023/BMA1x0/BMA250 3-Axis Accelerometer Driver"
depends on I2C && INPUT_BMA150=n

View File

@ -21,6 +21,9 @@ 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
obj-$(CONFIG_ADXL380) += adxl380.o
obj-$(CONFIG_ADXL380_I2C) += adxl380_i2c.o
obj-$(CONFIG_ADXL380_SPI) += adxl380_spi.o
obj-$(CONFIG_BMA180) += bma180.o
obj-$(CONFIG_BMA220) += bma220_spi.o
obj-$(CONFIG_BMA400) += bma400_core.o

View File

@ -1220,7 +1220,7 @@ static int adxl367_update_scan_mode(struct iio_dev *indio_dev,
return ret;
st->fifo_set_size = bitmap_weight(active_scan_mask,
indio_dev->masklength);
iio_get_masklength(indio_dev));
return 0;
}

View File

@ -72,7 +72,7 @@ static int adxl367_write(void *context, const void *val_buf, size_t val_size)
return spi_sync(st->spi, &st->reg_write_msg);
}
static struct regmap_bus adxl367_spi_regmap_bus = {
static const struct regmap_bus adxl367_spi_regmap_bus = {
.read = adxl367_read,
.write = adxl367_write,
};

View File

@ -1050,7 +1050,7 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
st->fifo_axis_mask = adxl372_axis_lookup_table[i].bits;
st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
indio_dev->masklength);
iio_get_masklength(indio_dev));
/* Configure the FIFO to store sets of impact event peak. */
if (st->peak_fifo_mode_en) {

1905
drivers/iio/accel/adxl380.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* ADXL380 3-Axis Digital Accelerometer
*
* Copyright 2024 Analog Devices Inc.
*/
#ifndef _ADXL380_H_
#define _ADXL380_H_
struct adxl380_chip_info {
const char *name;
const int scale_tbl[3][2];
const int samp_freq_tbl[3];
const int temp_offset;
const u16 chip_id;
};
extern const struct adxl380_chip_info adxl380_chip_info;
extern const struct adxl380_chip_info adxl382_chip_info;
int adxl380_probe(struct device *dev, struct regmap *regmap,
const struct adxl380_chip_info *chip_info);
bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg);
#endif /* _ADXL380_H_ */

View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* ADXL380 3-Axis Digital Accelerometer I2C driver
*
* Copyright 2024 Analog Devices Inc.
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "adxl380.h"
static const struct regmap_config adxl380_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.readable_noinc_reg = adxl380_readable_noinc_reg,
};
static int adxl380_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
const struct adxl380_chip_info *chip_data;
chip_data = i2c_get_match_data(client);
regmap = devm_regmap_init_i2c(client, &adxl380_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return adxl380_probe(&client->dev, regmap, chip_data);
}
static const struct i2c_device_id adxl380_i2c_id[] = {
{ "adxl380", (kernel_ulong_t)&adxl380_chip_info },
{ "adxl382", (kernel_ulong_t)&adxl382_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, adxl380_i2c_id);
static const struct of_device_id adxl380_of_match[] = {
{ .compatible = "adi,adxl380", .data = &adxl380_chip_info },
{ .compatible = "adi,adxl382", .data = &adxl382_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, adxl380_of_match);
static struct i2c_driver adxl380_i2c_driver = {
.driver = {
.name = "adxl380_i2c",
.of_match_table = adxl380_of_match,
},
.probe = adxl380_i2c_probe,
.id_table = adxl380_i2c_id,
};
module_i2c_driver(adxl380_i2c_driver);
MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADXL380 3-axis accelerometer I2C driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_ADXL380);

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* ADXL380 3-Axis Digital Accelerometer SPI driver
*
* Copyright 2024 Analog Devices Inc.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "adxl380.h"
static const struct regmap_config adxl380_spi_regmap_config = {
.reg_bits = 7,
.pad_bits = 1,
.val_bits = 8,
.read_flag_mask = BIT(0),
.readable_noinc_reg = adxl380_readable_noinc_reg,
};
static int adxl380_spi_probe(struct spi_device *spi)
{
const struct adxl380_chip_info *chip_data;
struct regmap *regmap;
chip_data = spi_get_device_match_data(spi);
regmap = devm_regmap_init_spi(spi, &adxl380_spi_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return adxl380_probe(&spi->dev, regmap, chip_data);
}
static const struct spi_device_id adxl380_spi_id[] = {
{ "adxl380", (kernel_ulong_t)&adxl380_chip_info },
{ "adxl382", (kernel_ulong_t)&adxl382_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl380_spi_id);
static const struct of_device_id adxl380_of_match[] = {
{ .compatible = "adi,adxl380", .data = &adxl380_chip_info },
{ .compatible = "adi,adxl382", .data = &adxl382_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, adxl380_of_match);
static struct spi_driver adxl380_spi_driver = {
.driver = {
.name = "adxl380_spi",
.of_match_table = adxl380_of_match,
},
.probe = adxl380_spi_probe,
.id_table = adxl380_spi_id,
};
module_spi_driver(adxl380_spi_driver);
MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADXL380 3-axis accelerometer SPI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_ADXL380);

View File

@ -876,8 +876,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
mutex_lock(&data->mutex);
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
ret = bma180_get_data_reg(data, bit);
if (ret < 0) {
mutex_unlock(&data->mutex);

View File

@ -13,6 +13,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -795,21 +796,19 @@ static int bma400_enable_steps(struct bma400_data *data, int val)
static int bma400_get_steps_reg(struct bma400_data *data, int *val)
{
u8 *steps_raw;
int ret;
steps_raw = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL);
u8 *steps_raw __free(kfree) = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL);
if (!steps_raw)
return -ENOMEM;
ret = regmap_bulk_read(data->regmap, BMA400_STEP_CNT0_REG,
steps_raw, BMA400_STEP_RAW_LEN);
if (ret) {
kfree(steps_raw);
if (ret)
return ret;
}
*val = get_unaligned_le24(steps_raw);
kfree(steps_raw);
return IIO_VAL_INT;
}

View File

@ -53,7 +53,7 @@ static int bma400_regmap_spi_write(void *context, const void *data,
return spi_write(spi, data, count);
}
static struct regmap_bus bma400_regmap_bus = {
static const struct regmap_bus bma400_regmap_bus = {
.read = bma400_regmap_spi_read,
.write = bma400_regmap_spi_write,
.read_flag_mask = BIT(7),

View File

@ -1007,8 +1007,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
int j, bit;
j = 0;
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength)
iio_for_each_active_channel(indio_dev, bit)
memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit],
sizeof(data->scan.channels[0]));

View File

@ -36,7 +36,7 @@ static int bmi088_regmap_spi_read(void *context, const void *reg,
return spi_write_then_read(spi, addr, sizeof(addr), val, val_size);
}
static struct regmap_bus bmi088_regmap_bus = {
static const struct regmap_bus bmi088_regmap_bus = {
.write = bmi088_regmap_spi_write,
.read = bmi088_regmap_spi_read,
};

View File

@ -62,7 +62,7 @@ static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev,
return ret;
}
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
for_each_set_bit(i, &scan_mask, iio_get_masklength(indio_dev)) {
*data = st->resp->dump.sensor[sensor_num].data[i] *
st->sign[i];
data++;

View File

@ -966,8 +966,7 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev)
int j, bit;
j = 0;
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit],
sizeof(data->scan.channels[0]));
}

View File

@ -173,6 +173,7 @@ enum kx_chipset {
KXCJ91008,
KXTJ21009,
KXTF9,
KX0221020,
KX0231025,
KX_MAX_CHIPS /* this must be last */
};
@ -580,8 +581,8 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
return ret;
}
/* On KX023, route all used interrupts to INT1 for now */
if (data->chipset == KX0231025 && data->client->irq > 0) {
/* On KX023 and KX022, route all used interrupts to INT1 for now */
if ((data->chipset == KX0231025 || data->chipset == KX0221020) && data->client->irq > 0) {
ret = i2c_smbus_write_byte_data(data->client, KX023_REG_INC4,
KX023_REG_INC4_DRDY1 |
KX023_REG_INC4_WUFI1);
@ -1507,6 +1508,7 @@ static int kxcjk1013_probe(struct i2c_client *client)
case KXTF9:
data->regs = &kxtf9_regs;
break;
case KX0221020:
case KX0231025:
data->regs = &kx0231025_regs;
break;
@ -1712,6 +1714,7 @@ static const struct i2c_device_id kxcjk1013_id[] = {
{"kxcj91008", KXCJ91008},
{"kxtj21009", KXTJ21009},
{"kxtf9", KXTF9},
{"kx022-1020", KX0221020},
{"kx023-1025", KX0231025},
{"SMO8500", KXCJ91008},
{}
@ -1724,6 +1727,7 @@ static const struct of_device_id kxcjk1013_of_match[] = {
{ .compatible = "kionix,kxcj91008", },
{ .compatible = "kionix,kxtj21009", },
{ .compatible = "kionix,kxtf9", },
{ .compatible = "kionix,kx022-1020", },
{ .compatible = "kionix,kx023-1025", },
{ }
};

View File

@ -900,8 +900,7 @@ static irqreturn_t msa311_buffer_thread(int irq, void *p)
mutex_lock(&msa311->lock);
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
chan = &msa311_channels[bit];
err = msa311_get_axis(msa311, chan, &axis);

View File

@ -494,8 +494,7 @@ static irqreturn_t sca3300_trigger_handler(int irq, void *p)
int bit, ret, val, i = 0;
s16 *channels = (s16 *)data->buffer;
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
ret = sca3300_read_reg(data, indio_dev->channels[bit].address, &val);
if (ret) {
dev_err_ratelimited(&data->spi->dev,

View File

@ -448,8 +448,7 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p)
goto err;
}
} else {
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
ret = stk8312_read_accel(data, bit);
if (ret < 0) {
mutex_unlock(&data->lock);

View File

@ -330,8 +330,7 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p)
goto err;
}
} else {
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
ret = stk8ba50_read_accel(data,
stk8ba50_channel_table[bit]);
if (ret < 0)

View File

@ -21,6 +21,18 @@ config AD_SIGMA_DELTA
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config AD4000
tristate "Analog Devices AD4000 ADC Driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD4000 high speed
SPI analog to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4000.
config AD4130
tristate "Analog Device AD4130 ADC Driver"
depends on SPI
@ -36,6 +48,17 @@ config AD4130
To compile this driver as a module, choose M here: the module will be
called ad4130.
config AD4695
tristate "Analog Device AD4695 ADC Driver"
depends on SPI
select REGMAP_SPI
help
Say yes here to build support for Analog Devices AD4695 and similar
analog to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4695.
config AD7091R
tristate
@ -991,6 +1014,19 @@ config NPCM_ADC
This driver can also be built as a module. If so, the module
will be called npcm_adc.
config PAC1921
tristate "Microchip Technology PAC1921 driver"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Microchip Technology's PAC1921
High-Side Power/Current Monitor with Analog Output.
This driver can also be built as a module. If so, the module
will be called pac1921.
config PAC1934
tristate "Microchip Technology PAC1934 driver"
depends on I2C
@ -1171,6 +1207,7 @@ config SD_ADC_MODULATOR
tristate "Generic sigma delta modulator"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_BACKEND
help
Select this option to enables sigma delta modulator. This driver can
support generic sigma delta modulators.
@ -1225,6 +1262,7 @@ config STM32_DFSDM_ADC
select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER
select IIO_TRIGGERED_BUFFER
select IIO_BACKEND
help
Select this option to support ADCSigma delta modulator for
STMicroelectronics STM32 digital filter for sigma delta converter.

View File

@ -6,7 +6,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4130) += ad4130.o
obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD7091R) += ad7091r-base.o
obj-$(CONFIG_AD7091R5) += ad7091r5.o
obj-$(CONFIG_AD7091R8) += ad7091r8.o
@ -90,6 +92,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PAC1921) += pac1921.o
obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o

722
drivers/iio/adc/ad4000.c Normal file
View File

@ -0,0 +1,722 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* AD4000 SPI ADC driver
*
* Copyright 2024 Analog Devices Inc.
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/byteorder/generic.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/util_macros.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define AD4000_READ_COMMAND 0x54
#define AD4000_WRITE_COMMAND 0x14
#define AD4000_CONFIG_REG_DEFAULT 0xE1
/* AD4000 Configuration Register programmable bits */
#define AD4000_CFG_SPAN_COMP BIT(3) /* Input span compression */
#define AD4000_CFG_HIGHZ BIT(2) /* High impedance mode */
#define AD4000_SCALE_OPTIONS 2
#define AD4000_TQUIET1_NS 190
#define AD4000_TQUIET2_NS 60
#define AD4000_TCONV_NS 320
#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.differential = 1, \
.channel = 0, \
.channel2 = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
.scan_type = { \
.sign = _sign, \
.realbits = _real_bits, \
.storagebits = _storage_bits, \
.shift = _storage_bits - _real_bits, \
.endianness = IIO_BE, \
}, \
}
#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \
__AD4000_DIFF_CHANNEL((_sign), (_real_bits), \
((_real_bits) > 16 ? 32 : 16), (_reg_access))
#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = 0, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
.scan_type = { \
.sign = _sign, \
.realbits = _real_bits, \
.storagebits = _storage_bits, \
.shift = _storage_bits - _real_bits, \
.endianness = IIO_BE, \
}, \
}
#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \
__AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \
((_real_bits) > 16 ? 32 : 16), (_reg_access))
static const char * const ad4000_power_supplies[] = {
"vdd", "vio"
};
enum ad4000_sdi {
AD4000_SDI_MOSI,
AD4000_SDI_VIO,
AD4000_SDI_CS,
AD4000_SDI_GND,
};
/* maps adi,sdi-pin property value to enum */
static const char * const ad4000_sdi_pin[] = {
[AD4000_SDI_MOSI] = "sdi",
[AD4000_SDI_VIO] = "high",
[AD4000_SDI_CS] = "cs",
[AD4000_SDI_GND] = "low",
};
/* Gains stored as fractions of 1000 so they can be expressed by integers. */
static const int ad4000_gains[] = {
454, 909, 1000, 1900,
};
struct ad4000_chip_info {
const char *dev_name;
struct iio_chan_spec chan_spec;
struct iio_chan_spec reg_access_chan_spec;
bool has_hardware_gain;
};
static const struct ad4000_chip_info ad4000_chip_info = {
.dev_name = "ad4000",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
};
static const struct ad4000_chip_info ad4001_chip_info = {
.dev_name = "ad4001",
.chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
};
static const struct ad4000_chip_info ad4002_chip_info = {
.dev_name = "ad4002",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
};
static const struct ad4000_chip_info ad4003_chip_info = {
.dev_name = "ad4003",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
};
static const struct ad4000_chip_info ad4004_chip_info = {
.dev_name = "ad4004",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
};
static const struct ad4000_chip_info ad4005_chip_info = {
.dev_name = "ad4005",
.chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
};
static const struct ad4000_chip_info ad4006_chip_info = {
.dev_name = "ad4006",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
};
static const struct ad4000_chip_info ad4007_chip_info = {
.dev_name = "ad4007",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
};
static const struct ad4000_chip_info ad4008_chip_info = {
.dev_name = "ad4008",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
};
static const struct ad4000_chip_info ad4010_chip_info = {
.dev_name = "ad4010",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
};
static const struct ad4000_chip_info ad4011_chip_info = {
.dev_name = "ad4011",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
};
static const struct ad4000_chip_info ad4020_chip_info = {
.dev_name = "ad4020",
.chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
};
static const struct ad4000_chip_info ad4021_chip_info = {
.dev_name = "ad4021",
.chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
};
static const struct ad4000_chip_info ad4022_chip_info = {
.dev_name = "ad4022",
.chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
};
static const struct ad4000_chip_info adaq4001_chip_info = {
.dev_name = "adaq4001",
.chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
.has_hardware_gain = true,
};
static const struct ad4000_chip_info adaq4003_chip_info = {
.dev_name = "adaq4003",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
.has_hardware_gain = true,
};
struct ad4000_state {
struct spi_device *spi;
struct gpio_desc *cnv_gpio;
struct spi_transfer xfers[2];
struct spi_message msg;
struct mutex lock; /* Protect read modify write cycle */
int vref_mv;
enum ad4000_sdi sdi_pin;
bool span_comp;
u16 gain_milli;
int scale_tbl[AD4000_SCALE_OPTIONS][2];
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
*/
struct {
union {
__be16 sample_buf16;
__be32 sample_buf32;
} data;
s64 timestamp __aligned(8);
} scan __aligned(IIO_DMA_MINALIGN);
u8 tx_buf[2];
u8 rx_buf[2];
};
static void ad4000_fill_scale_tbl(struct ad4000_state *st,
struct iio_chan_spec const *chan)
{
int val, tmp0, tmp1;
int scale_bits;
u64 tmp2;
/*
* ADCs that output two's complement code have one less bit to express
* voltage magnitude.
*/
if (chan->scan_type.sign == 's')
scale_bits = chan->scan_type.realbits - 1;
else
scale_bits = chan->scan_type.realbits;
/*
* The gain is stored as a fraction of 1000 and, as we need to
* divide vref_mv by the gain, we invert the gain/1000 fraction.
* Also multiply by an extra MILLI to preserve precision.
* Thus, we have MILLI * MILLI equals MICRO as fraction numerator.
*/
val = mult_frac(st->vref_mv, MICRO, st->gain_milli);
/* Would multiply by NANO here but we multiplied by extra MILLI */
tmp2 = shift_right((u64)val * MICRO, scale_bits);
tmp0 = div_s64_rem(tmp2, NANO, &tmp1);
/* Store scale for when span compression is disabled */
st->scale_tbl[0][0] = tmp0; /* Integer part */
st->scale_tbl[0][1] = abs(tmp1); /* Fractional part */
/* Store scale for when span compression is enabled */
st->scale_tbl[1][0] = tmp0;
/* The integer part is always zero so don't bother to divide it. */
if (chan->differential)
st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 4, 5);
else
st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 9, 10);
}
static int ad4000_write_reg(struct ad4000_state *st, uint8_t val)
{
st->tx_buf[0] = AD4000_WRITE_COMMAND;
st->tx_buf[1] = val;
return spi_write(st->spi, st->tx_buf, ARRAY_SIZE(st->tx_buf));
}
static int ad4000_read_reg(struct ad4000_state *st, unsigned int *val)
{
struct spi_transfer t = {
.tx_buf = st->tx_buf,
.rx_buf = st->rx_buf,
.len = 2,
};
int ret;
st->tx_buf[0] = AD4000_READ_COMMAND;
ret = spi_sync_transfer(st->spi, &t, 1);
if (ret < 0)
return ret;
*val = st->rx_buf[1];
return ret;
}
static int ad4000_convert_and_acquire(struct ad4000_state *st)
{
int ret;
/*
* In 4-wire mode, the CNV line is held high for the entire conversion
* and acquisition process. In other modes, the CNV GPIO is optional
* and, if provided, replaces controller CS. If CNV GPIO is not defined
* gpiod_set_value_cansleep() has no effect.
*/
gpiod_set_value_cansleep(st->cnv_gpio, 1);
ret = spi_sync(st->spi, &st->msg);
gpiod_set_value_cansleep(st->cnv_gpio, 0);
return ret;
}
static int ad4000_single_conversion(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val)
{
struct ad4000_state *st = iio_priv(indio_dev);
u32 sample;
int ret;
ret = ad4000_convert_and_acquire(st);
if (ret < 0)
return ret;
if (chan->scan_type.storagebits > 16)
sample = be32_to_cpu(st->scan.data.sample_buf32);
else
sample = be16_to_cpu(st->scan.data.sample_buf16);
sample >>= chan->scan_type.shift;
if (chan->scan_type.sign == 's')
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
return IIO_VAL_INT;
}
static int ad4000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long info)
{
struct ad4000_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_RAW:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return ad4000_single_conversion(indio_dev, chan, val);
unreachable();
case IIO_CHAN_INFO_SCALE:
*val = st->scale_tbl[st->span_comp][0];
*val2 = st->scale_tbl[st->span_comp][1];
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
*val = 0;
if (st->span_comp)
*val = mult_frac(st->vref_mv, 1, 10);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad4000_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long info)
{
struct ad4000_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SCALE:
*vals = (int *)st->scale_tbl;
*length = AD4000_SCALE_OPTIONS * 2;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int ad4000_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
default:
return IIO_VAL_INT_PLUS_MICRO;
}
}
static int ad4000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
struct ad4000_state *st = iio_priv(indio_dev);
unsigned int reg_val;
bool span_comp_en;
int ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
guard(mutex)(&st->lock);
ret = ad4000_read_reg(st, &reg_val);
if (ret < 0)
return ret;
span_comp_en = val2 == st->scale_tbl[1][1];
reg_val &= ~AD4000_CFG_SPAN_COMP;
reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en);
ret = ad4000_write_reg(st, reg_val);
if (ret < 0)
return ret;
st->span_comp = span_comp_en;
return 0;
}
unreachable();
default:
return -EINVAL;
}
}
static irqreturn_t ad4000_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad4000_state *st = iio_priv(indio_dev);
int ret;
ret = ad4000_convert_and_acquire(st);
if (ret < 0)
goto err_out;
iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp);
err_out:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct iio_info ad4000_reg_access_info = {
.read_raw = &ad4000_read_raw,
.read_avail = &ad4000_read_avail,
.write_raw = &ad4000_write_raw,
.write_raw_get_fmt = &ad4000_write_raw_get_fmt,
};
static const struct iio_info ad4000_info = {
.read_raw = &ad4000_read_raw,
};
/*
* This executes a data sample transfer for when the device connections are
* in "3-wire" mode, selected when the adi,sdi-pin device tree property is
* absent or set to "high". In this connection mode, the ADC SDI pin is
* connected to MOSI or to VIO and ADC CNV pin is connected either to a SPI
* controller CS or to a GPIO.
* AD4000 series of devices initiate conversions on the rising edge of CNV pin.
*
* If the CNV pin is connected to an SPI controller CS line (which is by default
* active low), the ADC readings would have a latency (delay) of one read.
* Moreover, since we also do ADC sampling for filling the buffer on triggered
* buffer mode, the timestamps of buffer readings would be disarranged.
* To prevent the read latency and reduce the time discrepancy between the
* sample read request and the time of actual sampling by the ADC, do a
* preparatory transfer to pulse the CS/CNV line.
*/
static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
const struct iio_chan_spec *chan)
{
unsigned int cnv_pulse_time = AD4000_TCONV_NS;
struct spi_transfer *xfers = st->xfers;
xfers[0].cs_change = 1;
xfers[0].cs_change_delay.value = cnv_pulse_time;
xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = &st->scan.data;
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
xfers[1].delay.value = AD4000_TQUIET2_NS;
xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
spi_message_init_with_transfers(&st->msg, st->xfers, 2);
return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg);
}
/*
* This executes a data sample transfer for when the device connections are
* in "4-wire" mode, selected when the adi,sdi-pin device tree property is
* set to "cs". In this connection mode, the controller CS pin is connected to
* ADC SDI pin and a GPIO is connected to ADC CNV pin.
* The GPIO connected to ADC CNV pin is set outside of the SPI transfer.
*/
static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st,
const struct iio_chan_spec *chan)
{
unsigned int cnv_to_sdi_time = AD4000_TCONV_NS;
struct spi_transfer *xfers = st->xfers;
/*
* Dummy transfer to cause enough delay between CNV going high and SDI
* going low.
*/
xfers[0].cs_off = 1;
xfers[0].delay.value = cnv_to_sdi_time;
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = &st->scan.data;
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
spi_message_init_with_transfers(&st->msg, st->xfers, 2);
return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg);
}
static int ad4000_config(struct ad4000_state *st)
{
unsigned int reg_val = AD4000_CONFIG_REG_DEFAULT;
if (device_property_present(&st->spi->dev, "adi,high-z-input"))
reg_val |= FIELD_PREP(AD4000_CFG_HIGHZ, 1);
return ad4000_write_reg(st, reg_val);
}
static int ad4000_probe(struct spi_device *spi)
{
const struct ad4000_chip_info *chip;
struct device *dev = &spi->dev;
struct iio_dev *indio_dev;
struct ad4000_state *st;
int gain_idx, ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
chip = spi_get_device_match_data(spi);
if (!chip)
return -EINVAL;
st = iio_priv(indio_dev);
st->spi = spi;
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies),
ad4000_power_supplies);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable power supplies\n");
ret = devm_regulator_get_enable_read_voltage(dev, "ref");
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to get ref regulator reference\n");
st->vref_mv = ret / 1000;
st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_HIGH);
if (IS_ERR(st->cnv_gpio))
return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
"Failed to get CNV GPIO");
ret = device_property_match_property_string(dev, "adi,sdi-pin",
ad4000_sdi_pin,
ARRAY_SIZE(ad4000_sdi_pin));
if (ret < 0 && ret != -EINVAL)
return dev_err_probe(dev, ret,
"getting adi,sdi-pin property failed\n");
/* Default to usual SPI connections if pin properties are not present */
st->sdi_pin = ret == -EINVAL ? AD4000_SDI_MOSI : ret;
switch (st->sdi_pin) {
case AD4000_SDI_MOSI:
indio_dev->info = &ad4000_reg_access_info;
indio_dev->channels = &chip->reg_access_chan_spec;
/*
* In "3-wire mode", the ADC SDI line must be kept high when
* data is not being clocked out of the controller.
* Request the SPI controller to make MOSI idle high.
*/
spi->mode |= SPI_MOSI_IDLE_HIGH;
ret = spi_setup(spi);
if (ret < 0)
return ret;
ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels);
if (ret)
return ret;
ret = ad4000_config(st);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to config device\n");
break;
case AD4000_SDI_VIO:
indio_dev->info = &ad4000_info;
indio_dev->channels = &chip->chan_spec;
ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels);
if (ret)
return ret;
break;
case AD4000_SDI_CS:
indio_dev->info = &ad4000_info;
indio_dev->channels = &chip->chan_spec;
ret = ad4000_prepare_4wire_mode_message(st, indio_dev->channels);
if (ret)
return ret;
break;
case AD4000_SDI_GND:
return dev_err_probe(dev, -EPROTONOSUPPORT,
"Unsupported connection mode\n");
default:
return dev_err_probe(dev, -EINVAL, "Unrecognized connection mode\n");
}
indio_dev->name = chip->dev_name;
indio_dev->num_channels = 1;
devm_mutex_init(dev, &st->lock);
st->gain_milli = 1000;
if (chip->has_hardware_gain) {
ret = device_property_read_u16(dev, "adi,gain-milli",
&st->gain_milli);
if (!ret) {
/* Match gain value from dt to one of supported gains */
gain_idx = find_closest(st->gain_milli, ad4000_gains,
ARRAY_SIZE(ad4000_gains));
st->gain_milli = ad4000_gains[gain_idx];
} else {
return dev_err_probe(dev, ret,
"Failed to read gain property\n");
}
}
ad4000_fill_scale_tbl(st, indio_dev->channels);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad4000_trigger_handler, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id ad4000_id[] = {
{ "ad4000", (kernel_ulong_t)&ad4000_chip_info },
{ "ad4001", (kernel_ulong_t)&ad4001_chip_info },
{ "ad4002", (kernel_ulong_t)&ad4002_chip_info },
{ "ad4003", (kernel_ulong_t)&ad4003_chip_info },
{ "ad4004", (kernel_ulong_t)&ad4004_chip_info },
{ "ad4005", (kernel_ulong_t)&ad4005_chip_info },
{ "ad4006", (kernel_ulong_t)&ad4006_chip_info },
{ "ad4007", (kernel_ulong_t)&ad4007_chip_info },
{ "ad4008", (kernel_ulong_t)&ad4008_chip_info },
{ "ad4010", (kernel_ulong_t)&ad4010_chip_info },
{ "ad4011", (kernel_ulong_t)&ad4011_chip_info },
{ "ad4020", (kernel_ulong_t)&ad4020_chip_info },
{ "ad4021", (kernel_ulong_t)&ad4021_chip_info },
{ "ad4022", (kernel_ulong_t)&ad4022_chip_info },
{ "adaq4001", (kernel_ulong_t)&adaq4001_chip_info },
{ "adaq4003", (kernel_ulong_t)&adaq4003_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4000_id);
static const struct of_device_id ad4000_of_match[] = {
{ .compatible = "adi,ad4000", .data = &ad4000_chip_info },
{ .compatible = "adi,ad4001", .data = &ad4001_chip_info },
{ .compatible = "adi,ad4002", .data = &ad4002_chip_info },
{ .compatible = "adi,ad4003", .data = &ad4003_chip_info },
{ .compatible = "adi,ad4004", .data = &ad4004_chip_info },
{ .compatible = "adi,ad4005", .data = &ad4005_chip_info },
{ .compatible = "adi,ad4006", .data = &ad4006_chip_info },
{ .compatible = "adi,ad4007", .data = &ad4007_chip_info },
{ .compatible = "adi,ad4008", .data = &ad4008_chip_info },
{ .compatible = "adi,ad4010", .data = &ad4010_chip_info },
{ .compatible = "adi,ad4011", .data = &ad4011_chip_info },
{ .compatible = "adi,ad4020", .data = &ad4020_chip_info },
{ .compatible = "adi,ad4021", .data = &ad4021_chip_info },
{ .compatible = "adi,ad4022", .data = &ad4022_chip_info },
{ .compatible = "adi,adaq4001", .data = &adaq4001_chip_info },
{ .compatible = "adi,adaq4003", .data = &adaq4003_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad4000_of_match);
static struct spi_driver ad4000_driver = {
.driver = {
.name = "ad4000",
.of_match_table = ad4000_of_match,
},
.probe = ad4000_probe,
.id_table = ad4000_id,
};
module_spi_driver(ad4000_driver);
MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD4000 ADC driver");
MODULE_LICENSE("GPL");

978
drivers/iio/adc/ad4695.c Normal file
View File

@ -0,0 +1,978 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SPI ADC driver for Analog Devices Inc. AD4695 and similar chips
*
* https://www.analog.com/en/products/ad4695.html
* https://www.analog.com/en/products/ad4696.html
* https://www.analog.com/en/products/ad4697.html
* https://www.analog.com/en/products/ad4698.html
*
* Copyright 2024 Analog Devices Inc.
* Copyright 2024 BayLibre, SAS
*/
#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <dt-bindings/iio/adi,ad4695.h>
/* AD4695 registers */
#define AD4695_REG_SPI_CONFIG_A 0x0000
#define AD4695_REG_SPI_CONFIG_A_SW_RST (BIT(7) | BIT(0))
#define AD4695_REG_SPI_CONFIG_B 0x0001
#define AD4695_REG_SPI_CONFIG_B_INST_MODE BIT(7)
#define AD4695_REG_DEVICE_TYPE 0x0003
#define AD4695_REG_SCRATCH_PAD 0x000A
#define AD4695_REG_VENDOR_L 0x000C
#define AD4695_REG_VENDOR_H 0x000D
#define AD4695_REG_LOOP_MODE 0x000E
#define AD4695_REG_SPI_CONFIG_C 0x0010
#define AD4695_REG_SPI_CONFIG_C_MB_STRICT BIT(7)
#define AD4695_REG_SPI_STATUS 0x0011
#define AD4695_REG_STATUS 0x0014
#define AD4695_REG_ALERT_STATUS1 0x0015
#define AD4695_REG_ALERT_STATUS2 0x0016
#define AD4695_REG_CLAMP_STATUS 0x001A
#define AD4695_REG_SETUP 0x0020
#define AD4695_REG_SETUP_LDO_EN BIT(4)
#define AD4695_REG_SETUP_SPI_MODE BIT(2)
#define AD4695_REG_SETUP_SPI_CYC_CTRL BIT(1)
#define AD4695_REG_REF_CTRL 0x0021
#define AD4695_REG_REF_CTRL_OV_MODE BIT(7)
#define AD4695_REG_REF_CTRL_VREF_SET GENMASK(4, 2)
#define AD4695_REG_REF_CTRL_REFHIZ_EN BIT(1)
#define AD4695_REG_REF_CTRL_REFBUF_EN BIT(0)
#define AD4695_REG_SEQ_CTRL 0x0022
#define AD4695_REG_SEQ_CTRL_STD_SEQ_EN BIT(7)
#define AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS GENMASK(6, 0)
#define AD4695_REG_AC_CTRL 0x0023
#define AD4695_REG_STD_SEQ_CONFIG 0x0024
#define AD4695_REG_GPIO_CTRL 0x0026
#define AD4695_REG_GP_MODE 0x0027
#define AD4695_REG_TEMP_CTRL 0x0029
#define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0)
#define AD4695_REG_CONFIG_IN(n) (0x0030 | (n))
#define AD4695_REG_CONFIG_IN_MODE BIT(6)
#define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4)
#define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3)
#define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n)))
#define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n)))
#define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n)))
#define AD4695_REG_OFFSET_IN(n) (0x00A0 | (2 * (n)))
#define AD4695_REG_GAIN_IN(n) (0x00C0 | (2 * (n)))
#define AD4695_REG_AS_SLOT(n) (0x0100 | (n))
#define AD4695_REG_AS_SLOT_INX GENMASK(3, 0)
#define AD4695_MAX_REG 0x017F
/* Conversion mode commands */
#define AD4695_CMD_EXIT_CNV_MODE 0x0A
#define AD4695_CMD_TEMP_CHAN 0x0F
#define AD4695_CMD_VOLTAGE_CHAN(n) (0x10 | (n))
/* timing specs */
#define AD4695_T_CONVERT_NS 415
#define AD4695_T_WAKEUP_HW_MS 3
#define AD4695_T_WAKEUP_SW_MS 3
#define AD4695_T_REFBUF_MS 100
#define AD4695_T_REGCONFIG_NS 20
#define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA)
/* Max number of voltage input channels. */
#define AD4695_MAX_CHANNELS 16
/* Max size of 1 raw sample in bytes. */
#define AD4695_MAX_CHANNEL_SIZE 2
enum ad4695_in_pair {
AD4695_IN_PAIR_REFGND,
AD4695_IN_PAIR_COM,
AD4695_IN_PAIR_EVEN_ODD,
};
struct ad4695_chip_info {
const char *name;
int max_sample_rate;
u32 t_acq_ns;
u8 num_voltage_inputs;
};
struct ad4695_channel_config {
unsigned int channel;
bool highz_en;
bool bipolar;
enum ad4695_in_pair pin_pairing;
unsigned int common_mode_mv;
};
struct ad4695_state {
struct spi_device *spi;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
/* voltages channels plus temperature and timestamp */
struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2];
struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS];
const struct ad4695_chip_info *chip_info;
/* Reference voltage. */
unsigned int vref_mv;
/* Common mode input pin voltage. */
unsigned int com_mv;
/* 1 per voltage and temperature chan plus 1 xfer to trigger 1st CNV */
struct spi_transfer buf_read_xfer[AD4695_MAX_CHANNELS + 2];
struct spi_message buf_read_msg;
/* Raw conversion data received. */
u8 buf[ALIGN((AD4695_MAX_CHANNELS + 2) * AD4695_MAX_CHANNEL_SIZE,
sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
u16 raw_data;
/* Commands to send for single conversion. */
u16 cnv_cmd;
u8 cnv_cmd2;
};
static const struct regmap_range ad4695_regmap_rd_ranges[] = {
regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B),
regmap_reg_range(AD4695_REG_DEVICE_TYPE, AD4695_REG_DEVICE_TYPE),
regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD),
regmap_reg_range(AD4695_REG_VENDOR_L, AD4695_REG_LOOP_MODE),
regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS),
regmap_reg_range(AD4695_REG_STATUS, AD4695_REG_ALERT_STATUS2),
regmap_reg_range(AD4695_REG_CLAMP_STATUS, AD4695_REG_CLAMP_STATUS),
regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_TEMP_CTRL),
regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_MAX_REG),
};
static const struct regmap_access_table ad4695_regmap_rd_table = {
.yes_ranges = ad4695_regmap_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(ad4695_regmap_rd_ranges),
};
static const struct regmap_range ad4695_regmap_wr_ranges[] = {
regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B),
regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD),
regmap_reg_range(AD4695_REG_LOOP_MODE, AD4695_REG_LOOP_MODE),
regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS),
regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_TEMP_CTRL),
regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_MAX_REG),
};
static const struct regmap_access_table ad4695_regmap_wr_table = {
.yes_ranges = ad4695_regmap_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(ad4695_regmap_wr_ranges),
};
static const struct regmap_config ad4695_regmap_config = {
.name = "ad4695",
.reg_bits = 16,
.val_bits = 8,
.max_register = AD4695_MAX_REG,
.rd_table = &ad4695_regmap_rd_table,
.wr_table = &ad4695_regmap_wr_table,
.can_multi_write = true,
};
static const struct iio_chan_spec ad4695_channel_template = {
.type = IIO_VOLTAGE,
.indexed = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
},
};
static const struct iio_chan_spec ad4695_temp_channel_template = {
.address = AD4695_CMD_TEMP_CHAN,
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
},
};
static const struct iio_chan_spec ad4695_soft_timestamp_channel_template =
IIO_CHAN_SOFT_TIMESTAMP(0);
static const char * const ad4695_power_supplies[] = {
"avdd", "vio"
};
static const struct ad4695_chip_info ad4695_chip_info = {
.name = "ad4695",
.max_sample_rate = 500 * KILO,
.t_acq_ns = 1715,
.num_voltage_inputs = 16,
};
static const struct ad4695_chip_info ad4696_chip_info = {
.name = "ad4696",
.max_sample_rate = 1 * MEGA,
.t_acq_ns = 715,
.num_voltage_inputs = 16,
};
static const struct ad4695_chip_info ad4697_chip_info = {
.name = "ad4697",
.max_sample_rate = 500 * KILO,
.t_acq_ns = 1715,
.num_voltage_inputs = 8,
};
static const struct ad4695_chip_info ad4698_chip_info = {
.name = "ad4698",
.max_sample_rate = 1 * MEGA,
.t_acq_ns = 715,
.num_voltage_inputs = 8,
};
/**
* ad4695_set_single_cycle_mode - Set the device in single cycle mode
* @st: The AD4695 state
* @channel: The first channel to read
*
* As per the datasheet, to enable single cycle mode, we need to set
* STD_SEQ_EN=0, NUM_SLOTS_AS=0 and CYC_CTRL=1 (Table 15). Setting SPI_MODE=1
* triggers the first conversion using the channel in AS_SLOT0.
*
* Context: can sleep, must be called with iio_device_claim_direct held
* Return: 0 on success, a negative error code on failure
*/
static int ad4695_set_single_cycle_mode(struct ad4695_state *st,
unsigned int channel)
{
int ret;
ret = regmap_clear_bits(st->regmap, AD4695_REG_SEQ_CTRL,
AD4695_REG_SEQ_CTRL_STD_SEQ_EN |
AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(0),
FIELD_PREP(AD4695_REG_AS_SLOT_INX, channel));
if (ret)
return ret;
return regmap_set_bits(st->regmap, AD4695_REG_SETUP,
AD4695_REG_SETUP_SPI_MODE |
AD4695_REG_SETUP_SPI_CYC_CTRL);
}
/**
* ad4695_enter_advanced_sequencer_mode - Put the ADC in advanced sequencer mode
* @st: The driver state
* @n: The number of slots to use - must be >= 2, <= 128
*
* As per the datasheet, to enable advanced sequencer, we need to set
* STD_SEQ_EN=0, NUM_SLOTS_AS=n-1 and CYC_CTRL=0 (Table 15). Setting SPI_MODE=1
* triggers the first conversion using the channel in AS_SLOT0.
*
* Return: 0 on success, a negative error code on failure
*/
static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n)
{
int ret;
ret = regmap_update_bits(st->regmap, AD4695_REG_SEQ_CTRL,
AD4695_REG_SEQ_CTRL_STD_SEQ_EN |
AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS,
FIELD_PREP(AD4695_REG_SEQ_CTRL_STD_SEQ_EN, 0) |
FIELD_PREP(AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, n - 1));
if (ret)
return ret;
return regmap_update_bits(st->regmap, AD4695_REG_SETUP,
AD4695_REG_SETUP_SPI_MODE | AD4695_REG_SETUP_SPI_CYC_CTRL,
FIELD_PREP(AD4695_REG_SETUP_SPI_MODE, 1) |
FIELD_PREP(AD4695_REG_SETUP_SPI_CYC_CTRL, 0));
}
/**
* ad4695_exit_conversion_mode - Exit conversion mode
* @st: The AD4695 state
*
* Sends SPI command to exit conversion mode.
*
* Return: 0 on success, a negative error code on failure
*/
static int ad4695_exit_conversion_mode(struct ad4695_state *st)
{
struct spi_transfer xfer = {
.tx_buf = &st->cnv_cmd2,
.len = 1,
.delay.value = AD4695_T_REGCONFIG_NS,
.delay.unit = SPI_DELAY_UNIT_NSECS,
};
/*
* Technically, could do a 5-bit transfer, but shifting to start of
* 8 bits instead for better SPI controller support.
*/
st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3;
return spi_sync_transfer(st->spi, &xfer, 1);
}
static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv)
{
u8 val;
if (vref_mv >= 2400 && vref_mv <= 2750)
val = 0;
else if (vref_mv > 2750 && vref_mv <= 3250)
val = 1;
else if (vref_mv > 3250 && vref_mv <= 3750)
val = 2;
else if (vref_mv > 3750 && vref_mv <= 4500)
val = 3;
else if (vref_mv > 4500 && vref_mv <= 5100)
val = 4;
else
return -EINVAL;
return regmap_update_bits(st->regmap, AD4695_REG_REF_CTRL,
AD4695_REG_REF_CTRL_VREF_SET,
FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val));
}
static int ad4695_write_chn_cfg(struct ad4695_state *st,
struct ad4695_channel_config *cfg)
{
u32 mask, val;
mask = AD4695_REG_CONFIG_IN_MODE;
val = FIELD_PREP(AD4695_REG_CONFIG_IN_MODE, cfg->bipolar ? 1 : 0);
mask |= AD4695_REG_CONFIG_IN_PAIR;
val |= FIELD_PREP(AD4695_REG_CONFIG_IN_PAIR, cfg->pin_pairing);
mask |= AD4695_REG_CONFIG_IN_AINHIGHZ_EN;
val |= FIELD_PREP(AD4695_REG_CONFIG_IN_AINHIGHZ_EN,
cfg->highz_en ? 1 : 0);
return regmap_update_bits(st->regmap,
AD4695_REG_CONFIG_IN(cfg->channel),
mask, val);
}
static int ad4695_buffer_preenable(struct iio_dev *indio_dev)
{
struct ad4695_state *st = iio_priv(indio_dev);
struct spi_transfer *xfer;
u8 temp_chan_bit = st->chip_info->num_voltage_inputs;
u32 bit, num_xfer, num_slots;
u32 temp_en = 0;
int ret;
/*
* We are using the advanced sequencer since it is the only way to read
* multiple channels that allows individual configuration of each
* voltage input channel. Slot 0 in the advanced sequencer is used to
* account for the gap between trigger polls - we don't read data from
* this slot. Each enabled voltage channel is assigned a slot starting
* with slot 1.
*/
num_slots = 1;
memset(st->buf_read_xfer, 0, sizeof(st->buf_read_xfer));
/* First xfer is only to trigger conversion of slot 1, so no rx. */
xfer = &st->buf_read_xfer[0];
xfer->cs_change = 1;
xfer->delay.value = st->chip_info->t_acq_ns;
xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
num_xfer = 1;
iio_for_each_active_channel(indio_dev, bit) {
xfer = &st->buf_read_xfer[num_xfer];
xfer->bits_per_word = 16;
xfer->rx_buf = &st->buf[(num_xfer - 1) * 2];
xfer->len = 2;
xfer->cs_change = 1;
xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
if (bit == temp_chan_bit) {
temp_en = 1;
} else {
ret = regmap_write(st->regmap,
AD4695_REG_AS_SLOT(num_slots),
FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit));
if (ret)
return ret;
num_slots++;
}
num_xfer++;
}
/*
* The advanced sequencer requires that at least 2 slots are enabled.
* Since slot 0 is always used for other purposes, we need only 1
* enabled voltage channel to meet this requirement. If the temperature
* channel is the only enabled channel, we need to add one more slot
* in the sequence but not read from it.
*/
if (num_slots < 2) {
/* move last xfer so we can insert one more xfer before it */
st->buf_read_xfer[num_xfer] = *xfer;
num_xfer++;
/* modify 2nd to last xfer for extra slot */
memset(xfer, 0, sizeof(*xfer));
xfer->cs_change = 1;
xfer->delay.value = st->chip_info->t_acq_ns;
xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
xfer++;
/* and add the extra slot in the sequencer */
ret = regmap_write(st->regmap,
AD4695_REG_AS_SLOT(num_slots),
FIELD_PREP(AD4695_REG_AS_SLOT_INX, 0));
if (ret)
return ret;
num_slots++;
}
/*
* Don't keep CS asserted after last xfer. Also triggers conversion of
* slot 0.
*/
xfer->cs_change = 0;
/*
* Temperature channel isn't included in the sequence, but rather
* controlled by setting a bit in the TEMP_CTRL register.
*/
ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL,
AD4695_REG_TEMP_CTRL_TEMP_EN,
FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, temp_en));
if (ret)
return ret;
spi_message_init_with_transfers(&st->buf_read_msg, st->buf_read_xfer,
num_xfer);
ret = spi_optimize_message(st->spi, &st->buf_read_msg);
if (ret)
return ret;
/* This triggers conversion of slot 0. */
ret = ad4695_enter_advanced_sequencer_mode(st, num_slots);
if (ret)
spi_unoptimize_message(&st->buf_read_msg);
return ret;
}
static int ad4695_buffer_postdisable(struct iio_dev *indio_dev)
{
struct ad4695_state *st = iio_priv(indio_dev);
int ret;
ret = ad4695_exit_conversion_mode(st);
if (ret)
return ret;
spi_unoptimize_message(&st->buf_read_msg);
return 0;
}
static const struct iio_buffer_setup_ops ad4695_buffer_setup_ops = {
.preenable = ad4695_buffer_preenable,
.postdisable = ad4695_buffer_postdisable,
};
static irqreturn_t ad4695_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad4695_state *st = iio_priv(indio_dev);
int ret;
ret = spi_sync(st->spi, &st->buf_read_msg);
if (ret)
goto out;
iio_push_to_buffers_with_timestamp(indio_dev, st->buf, pf->timestamp);
out:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
/**
* ad4695_read_one_sample - Read a single sample using single-cycle mode
* @st: The AD4695 state
* @address: The address of the channel to read
*
* Upon successful return, the sample will be stored in `st->raw_data`.
*
* Context: can sleep, must be called with iio_device_claim_direct held
* Return: 0 on success, a negative error code on failure
*/
static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address)
{
struct spi_transfer xfer[2] = { };
int ret, i = 0;
ret = ad4695_set_single_cycle_mode(st, address);
if (ret)
return ret;
/*
* Setting the first channel to the temperature channel isn't supported
* in single-cycle mode, so we have to do an extra xfer to read the
* temperature.
*/
if (address == AD4695_CMD_TEMP_CHAN) {
/* We aren't reading, so we can make this a short xfer. */
st->cnv_cmd2 = AD4695_CMD_TEMP_CHAN << 3;
xfer[0].tx_buf = &st->cnv_cmd2;
xfer[0].len = 1;
xfer[0].cs_change = 1;
xfer[0].cs_change_delay.value = AD4695_T_CONVERT_NS;
xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
i = 1;
}
/* Then read the result and exit conversion mode. */
st->cnv_cmd = AD4695_CMD_EXIT_CNV_MODE << 11;
xfer[i].bits_per_word = 16;
xfer[i].tx_buf = &st->cnv_cmd;
xfer[i].rx_buf = &st->raw_data;
xfer[i].len = 2;
return spi_sync_transfer(st->spi, xfer, i + 1);
}
static int ad4695_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ad4695_state *st = iio_priv(indio_dev);
struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index];
u8 realbits = chan->scan_type.realbits;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
ret = ad4695_read_one_sample(st, chan->address);
if (ret)
return ret;
if (chan->scan_type.sign == 's')
*val = sign_extend32(st->raw_data, realbits - 1);
else
*val = st->raw_data;
return IIO_VAL_INT;
}
unreachable();
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
*val = st->vref_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_TEMP:
/* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */
*val = st->vref_mv * -556;
*val2 = 16;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_VOLTAGE:
if (cfg->pin_pairing == AD4695_IN_PAIR_COM)
*val = st->com_mv * (1 << realbits) / st->vref_mv;
else if (cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD)
*val = cfg->common_mode_mv * (1 << realbits) / st->vref_mv;
else
*val = 0;
return IIO_VAL_INT;
case IIO_TEMP:
/* T_offset (°C) = -725 mV / (-1.8 mV/°C) */
/* T_offset (raw) = T_offset (°C) * (-1.8 mV/°C) * 2^16 / V_REF (mV) */
*val = -47513600;
*val2 = st->vref_mv;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct ad4695_state *st = iio_priv(indio_dev);
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_write(st->regmap, reg, writeval);
}
unreachable();
}
static const struct iio_info ad4695_info = {
.read_raw = &ad4695_read_raw,
.debugfs_reg_access = &ad4695_debugfs_reg_access,
};
static int ad4695_parse_channel_cfg(struct ad4695_state *st)
{
struct device *dev = &st->spi->dev;
struct ad4695_channel_config *chan_cfg;
struct iio_chan_spec *iio_chan;
int ret, i;
/* populate defaults */
for (i = 0; i < st->chip_info->num_voltage_inputs; i++) {
chan_cfg = &st->channels_cfg[i];
iio_chan = &st->iio_chan[i];
chan_cfg->highz_en = true;
chan_cfg->channel = i;
*iio_chan = ad4695_channel_template;
iio_chan->channel = i;
iio_chan->scan_index = i;
iio_chan->address = AD4695_CMD_VOLTAGE_CHAN(i);
}
/* modify based on firmware description */
device_for_each_child_node_scoped(dev, child) {
u32 reg, val;
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret)
return dev_err_probe(dev, ret,
"failed to read reg property (%s)\n",
fwnode_get_name(child));
if (reg >= st->chip_info->num_voltage_inputs)
return dev_err_probe(dev, -EINVAL,
"reg out of range (%s)\n",
fwnode_get_name(child));
iio_chan = &st->iio_chan[reg];
chan_cfg = &st->channels_cfg[reg];
chan_cfg->highz_en =
!fwnode_property_read_bool(child, "adi,no-high-z");
chan_cfg->bipolar = fwnode_property_read_bool(child, "bipolar");
ret = fwnode_property_read_u32(child, "common-mode-channel",
&val);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret,
"failed to read common-mode-channel (%s)\n",
fwnode_get_name(child));
if (ret == -EINVAL || val == AD4695_COMMON_MODE_REFGND)
chan_cfg->pin_pairing = AD4695_IN_PAIR_REFGND;
else if (val == AD4695_COMMON_MODE_COM)
chan_cfg->pin_pairing = AD4695_IN_PAIR_COM;
else
chan_cfg->pin_pairing = AD4695_IN_PAIR_EVEN_ODD;
if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD &&
val % 2 == 0)
return dev_err_probe(dev, -EINVAL,
"common-mode-channel must be odd number (%s)\n",
fwnode_get_name(child));
if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD &&
val != reg + 1)
return dev_err_probe(dev, -EINVAL,
"common-mode-channel must be next consecutive channel (%s)\n",
fwnode_get_name(child));
if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) {
char name[5];
snprintf(name, sizeof(name), "in%d", reg + 1);
ret = devm_regulator_get_enable_read_voltage(dev, name);
if (ret < 0)
return dev_err_probe(dev, ret,
"failed to get %s voltage (%s)\n",
name, fwnode_get_name(child));
chan_cfg->common_mode_mv = ret / 1000;
}
if (chan_cfg->bipolar &&
chan_cfg->pin_pairing == AD4695_IN_PAIR_REFGND)
return dev_err_probe(dev, -EINVAL,
"bipolar mode is not available for inputs paired with REFGND (%s).\n",
fwnode_get_name(child));
if (chan_cfg->bipolar)
iio_chan->scan_type.sign = 's';
ret = ad4695_write_chn_cfg(st, chan_cfg);
if (ret)
return ret;
}
/* Temperature channel must be next scan index after voltage channels. */
st->iio_chan[i] = ad4695_temp_channel_template;
st->iio_chan[i].scan_index = i;
i++;
st->iio_chan[i] = ad4695_soft_timestamp_channel_template;
st->iio_chan[i].scan_index = i;
return 0;
}
static int ad4695_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct ad4695_state *st;
struct iio_dev *indio_dev;
struct gpio_desc *cnv_gpio;
bool use_internal_ldo_supply;
bool use_internal_ref_buffer;
int ret;
cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
if (IS_ERR(cnv_gpio))
return dev_err_probe(dev, PTR_ERR(cnv_gpio),
"Failed to get CNV GPIO\n");
/* Driver currently requires CNV pin to be connected to SPI CS */
if (cnv_gpio)
return dev_err_probe(dev, -ENODEV,
"CNV GPIO is not supported\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
st->chip_info = spi_get_device_match_data(spi);
if (!st->chip_info)
return -EINVAL;
/* Registers cannot be read at the max allowable speed */
spi->max_speed_hz = AD4695_REG_ACCESS_SCLK_HZ;
st->regmap = devm_regmap_init_spi(spi, &ad4695_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
"Failed to initialize regmap\n");
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(ad4695_power_supplies),
ad4695_power_supplies);
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable power supplies\n");
/* If LDO_IN supply is present, then we are using internal LDO. */
ret = devm_regulator_get_enable_optional(dev, "ldo-in");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret,
"Failed to enable LDO_IN supply\n");
use_internal_ldo_supply = ret == 0;
if (!use_internal_ldo_supply) {
/* Otherwise we need an external VDD supply. */
ret = devm_regulator_get_enable(dev, "vdd");
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to enable VDD supply\n");
}
/* If REFIN supply is given, then we are using internal buffer */
ret = devm_regulator_get_enable_read_voltage(dev, "refin");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "Failed to get REFIN voltage\n");
if (ret != -ENODEV) {
st->vref_mv = ret / 1000;
use_internal_ref_buffer = true;
} else {
/* Otherwise, we need an external reference. */
ret = devm_regulator_get_enable_read_voltage(dev, "ref");
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to get REF voltage\n");
st->vref_mv = ret / 1000;
use_internal_ref_buffer = false;
}
ret = devm_regulator_get_enable_read_voltage(dev, "com");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "Failed to get COM voltage\n");
st->com_mv = ret == -ENODEV ? 0 : ret / 1000;
/*
* Reset the device using hardware reset if available or fall back to
* software reset.
*/
st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(st->reset_gpio))
return PTR_ERR(st->reset_gpio);
if (st->reset_gpio) {
gpiod_set_value(st->reset_gpio, 0);
msleep(AD4695_T_WAKEUP_HW_MS);
} else {
ret = regmap_write(st->regmap, AD4695_REG_SPI_CONFIG_A,
AD4695_REG_SPI_CONFIG_A_SW_RST);
if (ret)
return ret;
msleep(AD4695_T_WAKEUP_SW_MS);
}
/* Needed for debugfs since it only access registers 1 byte at a time. */
ret = regmap_set_bits(st->regmap, AD4695_REG_SPI_CONFIG_C,
AD4695_REG_SPI_CONFIG_C_MB_STRICT);
if (ret)
return ret;
/* Disable internal LDO if it isn't needed. */
ret = regmap_update_bits(st->regmap, AD4695_REG_SETUP,
AD4695_REG_SETUP_LDO_EN,
FIELD_PREP(AD4695_REG_SETUP_LDO_EN,
use_internal_ldo_supply ? 1 : 0));
if (ret)
return ret;
/* configure reference supply */
if (device_property_present(dev, "adi,no-ref-current-limit")) {
ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL,
AD4695_REG_REF_CTRL_OV_MODE);
if (ret)
return ret;
}
if (device_property_present(dev, "adi,no-ref-high-z")) {
if (use_internal_ref_buffer)
return dev_err_probe(dev, -EINVAL,
"Cannot disable high-Z mode for internal reference buffer\n");
ret = regmap_clear_bits(st->regmap, AD4695_REG_REF_CTRL,
AD4695_REG_REF_CTRL_REFHIZ_EN);
if (ret)
return ret;
}
ret = ad4695_set_ref_voltage(st, st->vref_mv);
if (ret)
return ret;
if (use_internal_ref_buffer) {
ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL,
AD4695_REG_REF_CTRL_REFBUF_EN);
if (ret)
return ret;
/* Give the capacitor some time to charge up. */
msleep(AD4695_T_REFBUF_MS);
}
ret = ad4695_parse_channel_cfg(st);
if (ret)
return ret;
indio_dev->name = st->chip_info->name;
indio_dev->info = &ad4695_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->iio_chan;
indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
ad4695_trigger_handler,
&ad4695_buffer_setup_ops);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id ad4695_spi_id_table[] = {
{ .name = "ad4695", .driver_data = (kernel_ulong_t)&ad4695_chip_info },
{ .name = "ad4696", .driver_data = (kernel_ulong_t)&ad4696_chip_info },
{ .name = "ad4697", .driver_data = (kernel_ulong_t)&ad4697_chip_info },
{ .name = "ad4698", .driver_data = (kernel_ulong_t)&ad4698_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4695_spi_id_table);
static const struct of_device_id ad4695_of_match_table[] = {
{ .compatible = "adi,ad4695", .data = &ad4695_chip_info, },
{ .compatible = "adi,ad4696", .data = &ad4696_chip_info, },
{ .compatible = "adi,ad4697", .data = &ad4697_chip_info, },
{ .compatible = "adi,ad4698", .data = &ad4698_chip_info, },
{ }
};
MODULE_DEVICE_TABLE(of, ad4695_of_match_table);
static struct spi_driver ad4695_driver = {
.driver = {
.name = "ad4695",
.of_match_table = ad4695_of_match_table,
},
.probe = ad4695_probe,
.id_table = ad4695_spi_id_table,
};
module_spi_driver(ad4695_driver);
MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver");
MODULE_LICENSE("GPL");

View File

@ -159,7 +159,7 @@ static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg,
return spi_write(spi, &st->tx_buf, 2);
}
static struct regmap_bus ad7091r8_regmap_bus = {
static const struct regmap_bus ad7091r8_regmap_bus = {
.reg_read = ad7091r_regmap_bus_reg_read,
.reg_write = ad7091r_regmap_bus_reg_write,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,

View File

@ -378,8 +378,7 @@ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channe
cfg->vref_mv = 2500;
st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK;
st->adc_control |= AD7124_ADC_CTRL_REF_EN(1);
return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL,
2, st->adc_control);
return 0;
default:
dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
return -EINVAL;
@ -397,24 +396,17 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co
tmp = (cfg->buf_positive << 1) + cfg->buf_negative;
val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) |
AD7124_CONFIG_IN_BUFF(tmp);
AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits);
ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val);
if (ret < 0)
return ret;
tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type);
ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_TYPE_MSK,
tmp, 3);
if (ret < 0)
return ret;
ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_FS_MSK,
AD7124_FILTER_FS(cfg->odr_sel_bits), 3);
if (ret < 0)
return ret;
return ad7124_spi_write_mask(st, AD7124_CONFIG(cfg->cfg_slot), AD7124_CONFIG_PGA_MSK,
AD7124_CONFIG_PGA(cfg->pga_bits), 2);
tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type) |
AD7124_FILTER_FS(cfg->odr_sel_bits);
return ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot),
AD7124_FILTER_TYPE_MSK | AD7124_FILTER_FS_MSK,
tmp, 3);
}
static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st)
@ -903,9 +895,9 @@ static int ad7124_setup(struct ad7124_state *st)
/* Set the power mode */
st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode);
ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
if (ret < 0)
return ret;
st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK;
st->adc_control |= AD7124_ADC_CTRL_MODE(AD_SD_MODE_IDLE);
mutex_init(&st->cfgs_lock);
INIT_KFIFO(st->live_cfgs_fifo);
@ -923,6 +915,10 @@ static int ad7124_setup(struct ad7124_state *st)
ad7124_set_channel_odr(st, i, 10);
}
ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
if (ret < 0)
return ret;
return ret;
}

View File

@ -8,6 +8,7 @@
#include <linux/interrupt.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@ -201,6 +202,7 @@ struct ad7192_chip_info {
struct ad7192_state {
const struct ad7192_chip_info *chip_info;
struct clk *mclk;
struct clk_hw int_clk_hw;
u16 int_vref_mv;
u32 aincom_mv;
u32 fclk;
@ -396,25 +398,162 @@ static inline bool ad7192_valid_external_frequency(u32 freq)
freq <= AD7192_EXT_FREQ_MHZ_MAX);
}
static int ad7192_clock_select(struct ad7192_state *st)
/*
* Position 0 of ad7192_clock_names, xtal, corresponds to clock source
* configuration AD7192_CLK_EXT_MCLK1_2 and position 1, mclk, corresponds to
* AD7192_CLK_EXT_MCLK2
*/
static const char *const ad7192_clock_names[] = {
"xtal",
"mclk"
};
static struct ad7192_state *clk_hw_to_ad7192(struct clk_hw *hw)
{
return container_of(hw, struct ad7192_state, int_clk_hw);
}
static unsigned long ad7192_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return AD7192_INT_FREQ_MHZ;
}
static int ad7192_clk_output_is_enabled(struct clk_hw *hw)
{
struct ad7192_state *st = clk_hw_to_ad7192(hw);
return st->clock_sel == AD7192_CLK_INT_CO;
}
static int ad7192_clk_prepare(struct clk_hw *hw)
{
struct ad7192_state *st = clk_hw_to_ad7192(hw);
int ret;
st->mode &= ~AD7192_MODE_CLKSRC_MASK;
st->mode |= AD7192_CLK_INT_CO;
ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
if (ret)
return ret;
st->clock_sel = AD7192_CLK_INT_CO;
return 0;
}
static void ad7192_clk_unprepare(struct clk_hw *hw)
{
struct ad7192_state *st = clk_hw_to_ad7192(hw);
int ret;
st->mode &= ~AD7192_MODE_CLKSRC_MASK;
st->mode |= AD7192_CLK_INT;
ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
if (ret)
return;
st->clock_sel = AD7192_CLK_INT;
}
static const struct clk_ops ad7192_int_clk_ops = {
.recalc_rate = ad7192_clk_recalc_rate,
.is_enabled = ad7192_clk_output_is_enabled,
.prepare = ad7192_clk_prepare,
.unprepare = ad7192_clk_unprepare,
};
static int ad7192_register_clk_provider(struct ad7192_state *st)
{
struct device *dev = &st->sd.spi->dev;
unsigned int clock_sel;
struct clk_init_data init = {};
int ret;
clock_sel = AD7192_CLK_INT;
if (!IS_ENABLED(CONFIG_COMMON_CLK))
return 0;
/* use internal clock */
if (!st->mclk) {
if (device_property_read_bool(dev, "adi,int-clock-output-enable"))
clock_sel = AD7192_CLK_INT_CO;
} else {
if (device_property_read_bool(dev, "adi,clock-xtal"))
clock_sel = AD7192_CLK_EXT_MCLK1_2;
else
clock_sel = AD7192_CLK_EXT_MCLK2;
if (!device_property_present(dev, "#clock-cells"))
return 0;
init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-clk",
fwnode_get_name(dev_fwnode(dev)));
if (!init.name)
return -ENOMEM;
init.ops = &ad7192_int_clk_ops;
st->int_clk_hw.init = &init;
ret = devm_clk_hw_register(dev, &st->int_clk_hw);
if (ret)
return ret;
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
&st->int_clk_hw);
}
static int ad7192_clock_setup(struct ad7192_state *st)
{
struct device *dev = &st->sd.spi->dev;
int ret;
/*
* The following two if branches are kept for backward compatibility but
* the use of the two devicetree properties is highly discouraged. Clock
* configuration should be done according to the bindings.
*/
if (device_property_read_bool(dev, "adi,int-clock-output-enable")) {
st->clock_sel = AD7192_CLK_INT_CO;
st->fclk = AD7192_INT_FREQ_MHZ;
dev_warn(dev, "Property adi,int-clock-output-enable is deprecated! Check bindings!\n");
return 0;
}
return clock_sel;
if (device_property_read_bool(dev, "adi,clock-xtal")) {
st->clock_sel = AD7192_CLK_EXT_MCLK1_2;
st->mclk = devm_clk_get_enabled(dev, "mclk");
if (IS_ERR(st->mclk))
return dev_err_probe(dev, PTR_ERR(st->mclk),
"Failed to get mclk\n");
st->fclk = clk_get_rate(st->mclk);
if (!ad7192_valid_external_frequency(st->fclk))
return dev_err_probe(dev, -EINVAL,
"External clock frequency out of bounds\n");
dev_warn(dev, "Property adi,clock-xtal is deprecated! Check bindings!\n");
return 0;
}
ret = device_property_match_property_string(dev, "clock-names",
ad7192_clock_names,
ARRAY_SIZE(ad7192_clock_names));
if (ret < 0) {
st->clock_sel = AD7192_CLK_INT;
st->fclk = AD7192_INT_FREQ_MHZ;
ret = ad7192_register_clk_provider(st);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register clock provider\n");
return 0;
}
st->clock_sel = AD7192_CLK_EXT_MCLK1_2 + ret;
st->mclk = devm_clk_get_enabled(dev, ad7192_clock_names[ret]);
if (IS_ERR(st->mclk))
return dev_err_probe(dev, PTR_ERR(st->mclk),
"Failed to get clock source\n");
st->fclk = clk_get_rate(st->mclk);
if (!ad7192_valid_external_frequency(st->fclk))
return dev_err_probe(dev, -EINVAL,
"External clock frequency out of bounds\n");
return 0;
}
static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
@ -1275,21 +1414,9 @@ static int ad7192_probe(struct spi_device *spi)
if (ret)
return ret;
st->fclk = AD7192_INT_FREQ_MHZ;
st->mclk = devm_clk_get_optional_enabled(dev, "mclk");
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
st->clock_sel = ad7192_clock_select(st);
if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
st->clock_sel == AD7192_CLK_EXT_MCLK2) {
st->fclk = clk_get_rate(st->mclk);
if (!ad7192_valid_external_frequency(st->fclk))
return dev_err_probe(dev, -EINVAL,
"External clock frequency out of bounds\n");
}
ret = ad7192_clock_setup(st);
if (ret)
return ret;
ret = ad7192_setup(indio_dev, dev);
if (ret)

View File

@ -123,7 +123,8 @@ static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad7266_state *st = iio_priv(indio_dev);
unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength);
unsigned int nr = find_first_bit(scan_mask,
iio_get_masklength(indio_dev));
ad7266_select_input(st, nr);

View File

@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/device.h>
@ -803,16 +804,16 @@ 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);
unsigned int *channels __free(kfree) = 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;
return IRQ_HANDLED;
for (i = 0; i < st->scan_cnt; i++) {
unsigned int val;
@ -852,9 +853,6 @@ static irqreturn_t ad7280_event_handler(int irq, void *private)
}
}
out:
kfree(channels);
return IRQ_HANDLED;
}

View File

@ -109,7 +109,8 @@ static int ad7298_update_scan_mode(struct iio_dev *indio_dev,
int scan_count;
/* Now compute overall size */
scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
scan_count = bitmap_weight(active_scan_mask,
iio_get_masklength(indio_dev));
command = AD7298_WRITE | st->ext_ref;

View File

@ -8,9 +8,11 @@
* Datasheets of supported parts:
* ad7380/1 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7380-7381.pdf
* ad7383/4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-7384.pdf
* ad7386/7/8 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7386-7387-7388.pdf
* ad7380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7380-4.pdf
* ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf
* ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf
* ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf
*/
#include <linux/align.h>
@ -31,7 +33,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define MAX_NUM_CHANNELS 4
#define MAX_NUM_CHANNELS 8
/* 2.5V internal reference voltage */
#define AD7380_INTERNAL_REF_MV 2500
@ -49,6 +51,8 @@
#define AD7380_REG_ADDR_ALERT_LOW_TH 0x4
#define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5
#define AD7380_CONFIG1_CH BIT(11)
#define AD7380_CONFIG1_SEQ BIT(10)
#define AD7380_CONFIG1_OS_MODE BIT(9)
#define AD7380_CONFIG1_OSR GENMASK(8, 6)
#define AD7380_CONFIG1_CRC_W BIT(5)
@ -80,6 +84,8 @@ struct ad7380_chip_info {
const char *name;
const struct iio_chan_spec *channels;
unsigned int num_channels;
unsigned int num_simult_channels;
bool has_mux;
const char * const *vcm_supplies;
unsigned int num_vcm_supplies;
const unsigned long *available_scan_masks;
@ -91,82 +97,151 @@ enum {
AD7380_SCAN_TYPE_RESOLUTION_BOOST,
};
/* Extended scan types for 14-bit chips. */
static const struct iio_scan_type ad7380_scan_type_14[] = {
/* Extended scan types for 12-bit unsigned chips. */
static const struct iio_scan_type ad7380_scan_type_12_u[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 'u',
.realbits = 12,
.storagebits = 16,
.endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 'u',
.realbits = 14,
.storagebits = 16,
.endianness = IIO_CPU,
},
};
/* Extended scan types for 14-bit signed chips. */
static const struct iio_scan_type ad7380_scan_type_14_s[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 's',
.realbits = 14,
.storagebits = 16,
.endianness = IIO_CPU
.endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU
.endianness = IIO_CPU,
},
};
/* Extended scan types for 16-bit chips. */
static const struct iio_scan_type ad7380_scan_type_16[] = {
/* Extended scan types for 14-bit unsigned chips. */
static const struct iio_scan_type ad7380_scan_type_14_u[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 'u',
.realbits = 14,
.storagebits = 16,
.endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
};
/* Extended scan types for 16-bit signed_chips. */
static const struct iio_scan_type ad7380_scan_type_16_s[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU
.endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 's',
.realbits = 18,
.storagebits = 32,
.endianness = IIO_CPU
.endianness = IIO_CPU,
},
};
#define AD7380_CHANNEL(index, bits, diff) { \
.type = IIO_VOLTAGE, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.indexed = 1, \
.differential = (diff), \
.channel = (diff) ? (2 * (index)) : (index), \
.channel2 = (diff) ? (2 * (index) + 1) : 0, \
.scan_index = (index), \
.has_ext_scan_type = 1, \
.ext_scan_type = ad7380_scan_type_##bits, \
.num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits),\
/* Extended scan types for 16-bit unsigned chips. */
static const struct iio_scan_type ad7380_scan_type_16_u[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 'u',
.realbits = 18,
.storagebits = 32,
.endianness = IIO_CPU,
},
};
#define AD7380_CHANNEL(index, bits, diff, sign) { \
.type = IIO_VOLTAGE, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.indexed = 1, \
.differential = (diff), \
.channel = (diff) ? (2 * (index)) : (index), \
.channel2 = (diff) ? (2 * (index) + 1) : 0, \
.scan_index = (index), \
.has_ext_scan_type = 1, \
.ext_scan_type = ad7380_scan_type_##bits##_##sign, \
.num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \
}
#define DEFINE_AD7380_2_CHANNEL(name, bits, diff) \
#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
AD7380_CHANNEL(0, bits, diff), \
AD7380_CHANNEL(1, bits, diff), \
AD7380_CHANNEL(0, bits, diff, sign), \
AD7380_CHANNEL(1, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(2), \
}
#define DEFINE_AD7380_4_CHANNEL(name, bits, diff) \
#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
AD7380_CHANNEL(0, bits, diff), \
AD7380_CHANNEL(1, bits, diff), \
AD7380_CHANNEL(2, bits, diff), \
AD7380_CHANNEL(3, bits, diff), \
AD7380_CHANNEL(0, bits, diff, sign), \
AD7380_CHANNEL(1, bits, diff, sign), \
AD7380_CHANNEL(2, bits, diff, sign), \
AD7380_CHANNEL(3, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(4), \
}
#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
AD7380_CHANNEL(0, bits, diff, sign), \
AD7380_CHANNEL(1, bits, diff, sign), \
AD7380_CHANNEL(2, bits, diff, sign), \
AD7380_CHANNEL(3, bits, diff, sign), \
AD7380_CHANNEL(4, bits, diff, sign), \
AD7380_CHANNEL(5, bits, diff, sign), \
AD7380_CHANNEL(6, bits, diff, sign), \
AD7380_CHANNEL(7, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(8), \
}
/* fully differential */
DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1);
DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1);
DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1);
DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1);
DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s);
DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s);
DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s);
DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s);
/* pseudo differential */
DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0);
DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0);
DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0);
DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0);
DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s);
DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s);
DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0, s);
DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0, s);
/* Single ended */
DEFINE_AD7380_4_CHANNEL(ad7386_channels, 16, 0, u);
DEFINE_AD7380_4_CHANNEL(ad7387_channels, 14, 0, u);
DEFINE_AD7380_4_CHANNEL(ad7388_channels, 12, 0, u);
DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u);
DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u);
DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u);
static const char * const ad7380_2_channel_vcm_supplies[] = {
"aina", "ainb",
@ -187,6 +262,60 @@ static const unsigned long ad7380_4_channel_scan_masks[] = {
0
};
/*
* Single ended parts have a 2:1 multiplexer in front of each ADC.
*
* From an IIO point of view, all inputs are exported, i.e ad7386/7/8
* export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
*
* Inputs AinX0 of multiplexers correspond to the first half of IIO channels
* (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
* 4-7). Example for AD7386/7/8 (2 channels parts):
*
* IIO | AD7386/7/8
* | +----------------------------
* | | _____ ______
* | | | | | |
* voltage0 | AinA0 --|--->| | | |
* | | | mux |----->| ADCA |---
* voltage2 | AinA1 --|--->| | | |
* | | |_____| |_____ |
* | | _____ ______
* | | | | | |
* voltage1 | AinB0 --|--->| | | |
* | | | mux |----->| ADCB |---
* voltage3 | AinB1 --|--->| | | |
* | | |_____| |______|
* | |
* | +----------------------------
*
* Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
* scan masks.
* When sequencer mode is enabled, chip automatically cycles through
* AinX0 and AinX1 channels. From an IIO point of view, we ca enable all
* channels, at the cost of an extra read, thus dividing the maximum rate by
* two.
*/
enum {
AD7380_SCAN_MASK_CH_0,
AD7380_SCAN_MASK_CH_1,
AD7380_SCAN_MASK_SEQ,
};
static const unsigned long ad7380_2x2_channel_scan_masks[] = {
[AD7380_SCAN_MASK_CH_0] = GENMASK(1, 0),
[AD7380_SCAN_MASK_CH_1] = GENMASK(3, 2),
[AD7380_SCAN_MASK_SEQ] = GENMASK(3, 0),
0
};
static const unsigned long ad7380_2x4_channel_scan_masks[] = {
[AD7380_SCAN_MASK_CH_0] = GENMASK(3, 0),
[AD7380_SCAN_MASK_CH_1] = GENMASK(7, 4),
[AD7380_SCAN_MASK_SEQ] = GENMASK(7, 0),
0
};
static const struct ad7380_timing_specs ad7380_timing = {
.t_csh_ns = 10,
};
@ -208,6 +337,7 @@ static const struct ad7380_chip_info ad7380_chip_info = {
.name = "ad7380",
.channels = ad7380_channels,
.num_channels = ARRAY_SIZE(ad7380_channels),
.num_simult_channels = 2,
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
@ -216,6 +346,7 @@ static const struct ad7380_chip_info ad7381_chip_info = {
.name = "ad7381",
.channels = ad7381_channels,
.num_channels = ARRAY_SIZE(ad7381_channels),
.num_simult_channels = 2,
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
@ -224,6 +355,7 @@ static const struct ad7380_chip_info ad7383_chip_info = {
.name = "ad7383",
.channels = ad7383_channels,
.num_channels = ARRAY_SIZE(ad7383_channels),
.num_simult_channels = 2,
.vcm_supplies = ad7380_2_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
@ -234,16 +366,48 @@ static const struct ad7380_chip_info ad7384_chip_info = {
.name = "ad7384",
.channels = ad7384_channels,
.num_channels = ARRAY_SIZE(ad7384_channels),
.num_simult_channels = 2,
.vcm_supplies = ad7380_2_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
static const struct ad7380_chip_info ad7386_chip_info = {
.name = "ad7386",
.channels = ad7386_channels,
.num_channels = ARRAY_SIZE(ad7386_channels),
.num_simult_channels = 2,
.has_mux = true,
.available_scan_masks = ad7380_2x2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
static const struct ad7380_chip_info ad7387_chip_info = {
.name = "ad7387",
.channels = ad7387_channels,
.num_channels = ARRAY_SIZE(ad7387_channels),
.num_simult_channels = 2,
.has_mux = true,
.available_scan_masks = ad7380_2x2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
static const struct ad7380_chip_info ad7388_chip_info = {
.name = "ad7388",
.channels = ad7388_channels,
.num_channels = ARRAY_SIZE(ad7388_channels),
.num_simult_channels = 2,
.has_mux = true,
.available_scan_masks = ad7380_2x2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
static const struct ad7380_chip_info ad7380_4_chip_info = {
.name = "ad7380-4",
.channels = ad7380_4_channels,
.num_channels = ARRAY_SIZE(ad7380_4_channels),
.num_simult_channels = 4,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
@ -252,6 +416,7 @@ static const struct ad7380_chip_info ad7381_4_chip_info = {
.name = "ad7381-4",
.channels = ad7381_4_channels,
.num_channels = ARRAY_SIZE(ad7381_4_channels),
.num_simult_channels = 4,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
@ -260,6 +425,7 @@ static const struct ad7380_chip_info ad7383_4_chip_info = {
.name = "ad7383-4",
.channels = ad7383_4_channels,
.num_channels = ARRAY_SIZE(ad7383_4_channels),
.num_simult_channels = 4,
.vcm_supplies = ad7380_4_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
@ -270,23 +436,58 @@ static const struct ad7380_chip_info ad7384_4_chip_info = {
.name = "ad7384-4",
.channels = ad7384_4_channels,
.num_channels = ARRAY_SIZE(ad7384_4_channels),
.num_simult_channels = 4,
.vcm_supplies = ad7380_4_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
static const struct ad7380_chip_info ad7386_4_chip_info = {
.name = "ad7386-4",
.channels = ad7386_4_channels,
.num_channels = ARRAY_SIZE(ad7386_4_channels),
.num_simult_channels = 4,
.has_mux = true,
.available_scan_masks = ad7380_2x4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
static const struct ad7380_chip_info ad7387_4_chip_info = {
.name = "ad7387-4",
.channels = ad7387_4_channels,
.num_channels = ARRAY_SIZE(ad7387_4_channels),
.num_simult_channels = 4,
.has_mux = true,
.available_scan_masks = ad7380_2x4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
static const struct ad7380_chip_info ad7388_4_chip_info = {
.name = "ad7388-4",
.channels = ad7388_4_channels,
.num_channels = ARRAY_SIZE(ad7388_4_channels),
.num_simult_channels = 4,
.has_mux = true,
.available_scan_masks = ad7380_2x4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
struct ad7380_state {
const struct ad7380_chip_info *chip_info;
struct spi_device *spi;
struct regmap *regmap;
unsigned int oversampling_ratio;
bool resolution_boost_enabled;
unsigned int ch;
bool seq;
unsigned int vref_mv;
unsigned int vcm_mv[MAX_NUM_CHANNELS];
/* xfers, message an buffer for reading sample data */
struct spi_transfer xfer[2];
struct spi_message msg;
struct spi_transfer normal_xfer[2];
struct spi_message normal_msg;
struct spi_transfer seq_xfer[4];
struct spi_message seq_msg;
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
@ -379,6 +580,43 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
unreachable();
}
/*
* When switching channel, the ADC require an additional settling time.
* According to the datasheet, data is value on the third CS low. We already
* have an extra toggle before each read (either direct reads or buffered reads)
* to sample correct data, so we just add a single CS toggle at the end of the
* register write.
*/
static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
{
struct spi_transfer xfer = {
.delay = {
.value = T_CONVERT_NS,
.unit = SPI_DELAY_UNIT_NSECS,
}
};
int ret;
if (st->ch == ch)
return 0;
ret = regmap_update_bits(st->regmap,
AD7380_REG_ADDR_CONFIG1,
AD7380_CONFIG1_CH,
FIELD_PREP(AD7380_CONFIG1_CH, ch));
if (ret)
return ret;
st->ch = ch;
if (st->oversampling_ratio > 1)
xfer.delay.value = T_CONVERT_0_NS +
T_CONVERT_X_NS * (st->oversampling_ratio - 1);
return spi_sync_transfer(st->spi, &xfer, 1);
}
/**
* ad7380_update_xfers - update the SPI transfers base on the current scan type
* @st: device instance specific state
@ -387,33 +625,47 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
static void ad7380_update_xfers(struct ad7380_state *st,
const struct iio_scan_type *scan_type)
{
struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer;
unsigned int t_convert = T_CONVERT_NS;
/*
* First xfer only triggers conversion and has to be long enough for
* all conversions to complete, which can be multiple conversion in the
* case of oversampling. Technically T_CONVERT_X_NS is lower for some
* chips, but we use the maximum value for simplicity for now.
* In the case of oversampling, conversion time is higher than in normal
* mode. Technically T_CONVERT_X_NS is lower for some chips, but we use
* the maximum value for simplicity for now.
*/
if (st->oversampling_ratio > 1)
st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS *
(st->oversampling_ratio - 1);
else
st->xfer[0].delay.value = T_CONVERT_NS;
t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
(st->oversampling_ratio - 1);
st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
/*
* Second xfer reads all channels. Data size depends on if resolution
* boost is enabled or not.
*/
st->xfer[1].bits_per_word = scan_type->realbits;
st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
(st->chip_info->num_channels - 1);
if (st->seq) {
xfer[0].delay.value = xfer[1].delay.value = t_convert;
xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
xfer[2].bits_per_word = xfer[3].bits_per_word =
scan_type->realbits;
xfer[2].len = xfer[3].len =
BITS_TO_BYTES(scan_type->storagebits) *
st->chip_info->num_simult_channels;
xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len;
/* Additional delay required here when oversampling is enabled */
if (st->oversampling_ratio > 1)
xfer[2].delay.value = t_convert;
else
xfer[2].delay.value = 0;
xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS;
} else {
xfer[0].delay.value = t_convert;
xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfer[1].bits_per_word = scan_type->realbits;
xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
st->chip_info->num_simult_channels;
}
}
static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
struct spi_message *msg = &st->normal_msg;
/*
* Currently, we always read all channels at the same time. The scan_type
@ -423,16 +675,63 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
if (IS_ERR(scan_type))
return PTR_ERR(scan_type);
if (st->chip_info->has_mux) {
unsigned int index;
int ret;
/*
* Depending on the requested scan_mask and current state,
* we need to either change CH bit, or enable sequencer mode
* to sample correct data.
* Sequencer mode is enabled if active mask corresponds to all
* IIO channels enabled. Otherwise, CH bit is set.
*/
ret = iio_active_scan_mask_index(indio_dev);
if (ret < 0)
return ret;
index = ret;
if (index == AD7380_SCAN_MASK_SEQ) {
ret = regmap_update_bits(st->regmap,
AD7380_REG_ADDR_CONFIG1,
AD7380_CONFIG1_SEQ,
FIELD_PREP(AD7380_CONFIG1_SEQ, 1));
if (ret)
return ret;
msg = &st->seq_msg;
st->seq = true;
} else {
ret = ad7380_set_ch(st, index);
if (ret)
return ret;
}
}
ad7380_update_xfers(st, scan_type);
return spi_optimize_message(st->spi, &st->msg);
return spi_optimize_message(st->spi, msg);
}
static int ad7380_triggered_buffer_postdisable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
struct spi_message *msg = &st->normal_msg;
int ret;
spi_unoptimize_message(&st->msg);
if (st->seq) {
ret = regmap_update_bits(st->regmap,
AD7380_REG_ADDR_CONFIG1,
AD7380_CONFIG1_SEQ,
FIELD_PREP(AD7380_CONFIG1_SEQ, 0));
if (ret)
return ret;
msg = &st->seq_msg;
st->seq = false;
}
spi_unoptimize_message(msg);
return 0;
}
@ -447,9 +746,10 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7380_state *st = iio_priv(indio_dev);
struct spi_message *msg = st->seq ? &st->seq_msg : &st->normal_msg;
int ret;
ret = spi_sync(st->spi, &st->msg);
ret = spi_sync(st->spi, msg);
if (ret)
goto out;
@ -465,20 +765,43 @@ out:
static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index,
const struct iio_scan_type *scan_type, int *val)
{
unsigned int index = scan_index;
int ret;
if (st->chip_info->has_mux) {
unsigned int ch = 0;
if (index >= st->chip_info->num_simult_channels) {
index -= st->chip_info->num_simult_channels;
ch = 1;
}
ret = ad7380_set_ch(st, ch);
if (ret)
return ret;
}
ad7380_update_xfers(st, scan_type);
ret = spi_sync(st->spi, &st->msg);
ret = spi_sync(st->spi, &st->normal_msg);
if (ret < 0)
return ret;
if (scan_type->storagebits > 16)
*val = sign_extend32(*(u32 *)(st->scan_data + 4 * scan_index),
scan_type->realbits - 1);
else
*val = sign_extend32(*(u16 *)(st->scan_data + 2 * scan_index),
scan_type->realbits - 1);
if (scan_type->storagebits > 16) {
if (scan_type->sign == 's')
*val = sign_extend32(*(u32 *)(st->scan_data + 4 * index),
scan_type->realbits - 1);
else
*val = *(u32 *)(st->scan_data + 4 * index) &
GENMASK(scan_type->realbits - 1, 0);
} else {
if (scan_type->sign == 's')
*val = sign_extend32(*(u16 *)(st->scan_data + 2 * index),
scan_type->realbits - 1);
else
*val = *(u16 *)(st->scan_data + 2 * index) &
GENMASK(scan_type->realbits - 1, 0);
}
return IIO_VAL_INT;
}
@ -655,6 +978,8 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref)
/* This is the default value after reset. */
st->oversampling_ratio = 1;
st->ch = 0;
st->seq = false;
/* SPI 1-wire mode */
return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
@ -756,21 +1081,45 @@ static int ad7380_probe(struct spi_device *spi)
"failed to allocate register map\n");
/*
* Setting up a low latency read for getting sample data. Used for both
* direct read an triggered buffer. Additional fields will be set up in
* ad7380_update_xfers() based on the current state of the driver at the
* time of the read.
* Setting up xfer structures for both normal and sequence mode. These
* struct are used for both direct read and triggered buffer. Additional
* fields will be set up in ad7380_update_xfers() based on the current
* state of the driver at the time of the read.
*/
/* toggle CS (no data xfer) to trigger a conversion */
st->xfer[0].cs_change = 1;
st->xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
/*
* In normal mode a read is composed of two steps:
* - first, toggle CS (no data xfer) to trigger a conversion
* - then, read data
*/
st->normal_xfer[0].cs_change = 1;
st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
st->normal_xfer[1].rx_buf = st->scan_data;
/* then do a second xfer to read the data */
st->xfer[1].rx_buf = st->scan_data;
spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer,
ARRAY_SIZE(st->normal_xfer));
/*
* In sequencer mode a read is composed of four steps:
* - CS toggle (no data xfer) to get the right point in the sequence
* - CS toggle (no data xfer) to trigger a conversion of AinX0 and
* acquisition of AinX1
* - 2 data reads, to read AinX0 and AinX1
*/
st->seq_xfer[0].cs_change = 1;
st->seq_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->seq_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
st->seq_xfer[1].cs_change = 1;
st->seq_xfer[1].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->seq_xfer[1].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
spi_message_init_with_transfers(&st->msg, st->xfer, ARRAY_SIZE(st->xfer));
st->seq_xfer[2].rx_buf = st->scan_data;
st->seq_xfer[2].cs_change = 1;
st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer,
ARRAY_SIZE(st->seq_xfer));
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
@ -798,10 +1147,16 @@ static const struct of_device_id ad7380_of_match_table[] = {
{ .compatible = "adi,ad7381", .data = &ad7381_chip_info },
{ .compatible = "adi,ad7383", .data = &ad7383_chip_info },
{ .compatible = "adi,ad7384", .data = &ad7384_chip_info },
{ .compatible = "adi,ad7386", .data = &ad7386_chip_info },
{ .compatible = "adi,ad7387", .data = &ad7387_chip_info },
{ .compatible = "adi,ad7388", .data = &ad7388_chip_info },
{ .compatible = "adi,ad7380-4", .data = &ad7380_4_chip_info },
{ .compatible = "adi,ad7381-4", .data = &ad7381_4_chip_info },
{ .compatible = "adi,ad7383-4", .data = &ad7383_4_chip_info },
{ .compatible = "adi,ad7384-4", .data = &ad7384_4_chip_info },
{ .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info },
{ .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info },
{ .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info },
{ }
};
@ -810,10 +1165,16 @@ static const struct spi_device_id ad7380_id_table[] = {
{ "ad7381", (kernel_ulong_t)&ad7381_chip_info },
{ "ad7383", (kernel_ulong_t)&ad7383_chip_info },
{ "ad7384", (kernel_ulong_t)&ad7384_chip_info },
{ "ad7386", (kernel_ulong_t)&ad7386_chip_info },
{ "ad7387", (kernel_ulong_t)&ad7387_chip_info },
{ "ad7388", (kernel_ulong_t)&ad7388_chip_info },
{ "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info },
{ "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info },
{ "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info },
{ "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info },
{ "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info },
{ "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info },
{ "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7380_id_table);

View File

@ -69,19 +69,17 @@ static int ad7606_reg_access(struct iio_dev *indio_dev,
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
if (readval) {
ret = st->bops->reg_read(st, reg);
if (ret < 0)
goto err_unlock;
return ret;
*readval = ret;
ret = 0;
return 0;
} else {
ret = st->bops->reg_write(st, reg, writeval);
return st->bops->reg_write(st, reg, writeval);
}
err_unlock:
mutex_unlock(&st->lock);
return ret;
}
static int ad7606_read_samples(struct ad7606_state *st)
@ -124,19 +122,19 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p)
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = ad7606_read_samples(st);
if (ret == 0)
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
iio_get_time_ns(indio_dev));
if (ret)
goto error_ret;
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
iio_get_time_ns(indio_dev));
error_ret:
iio_trigger_notify_done(indio_dev->trig);
/* The rising edge of the CONVST signal starts a new conversion. */
gpiod_set_value(st->gpio_convst, 1);
mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
@ -236,9 +234,9 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
struct ad7606_state *st = iio_priv(indio_dev);
DECLARE_BITMAP(values, 3);
values[0] = val;
values[0] = val & GENMASK(2, 0);
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc,
st->gpio_os->info, values);
/* AD7616 requires a reset to update value */
@ -257,19 +255,17 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
struct ad7606_state *st = iio_priv(indio_dev);
int i, ret, ch = 0;
guard(mutex)(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
mutex_lock(&st->lock);
i = find_closest(val2, st->scale_avail, st->num_scales);
if (st->sw_mode_en)
ch = chan->address;
ret = st->write_scale(indio_dev, ch, i);
if (ret < 0) {
mutex_unlock(&st->lock);
if (ret < 0)
return ret;
}
st->range[ch] = i;
mutex_unlock(&st->lock);
return 0;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@ -277,14 +273,9 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
mutex_lock(&st->lock);
ret = st->write_os(indio_dev, i);
if (ret < 0) {
mutex_unlock(&st->lock);
if (ret < 0)
return ret;
}
st->oversampling = st->oversampling_avail[i];
mutex_unlock(&st->lock);
return 0;
default:
@ -443,7 +434,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
return PTR_ERR(st->gpio_range);
st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
GPIOD_OUT_HIGH);
GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_standby))
return PTR_ERR(st->gpio_standby);
@ -686,7 +677,7 @@ static int ad7606_suspend(struct device *dev)
if (st->gpio_standby) {
gpiod_set_value(st->gpio_range, 1);
gpiod_set_value(st->gpio_standby, 0);
gpiod_set_value(st->gpio_standby, 1);
}
return 0;

View File

@ -249,8 +249,9 @@ static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned long os[3] = {1};
DECLARE_BITMAP(os, 3);
bitmap_fill(os, 3);
/*
* Software mode is enabled when all three oversampling
* pins are set to high. If oversampling gpios are defined
@ -258,7 +259,7 @@ static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
* otherwise, they must be hardwired to VDD
*/
if (st->gpio_os) {
gpiod_set_array_value(ARRAY_SIZE(os),
gpiod_set_array_value(st->gpio_os->ndescs,
st->gpio_os->desc, st->gpio_os->info, os);
}
/* OS of 128 and 256 are available only in software mode */

View File

@ -544,13 +544,10 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev,
{
struct ad7768_state *st = iio_priv(indio_dev);
struct device *device = indio_dev->dev.parent;
struct fwnode_handle *fwnode;
struct fwnode_handle *child;
const char *label;
int crt_ch = 0;
fwnode = dev_fwnode(device);
fwnode_for_each_child_node(fwnode, child) {
device_for_each_child_node_scoped(device, child) {
if (fwnode_property_read_u32(child, "reg", &crt_ch))
continue;

View File

@ -237,7 +237,8 @@ static int ad799x_update_scan_mode(struct iio_dev *indio_dev,
if (!st->rx_buf)
return -ENOMEM;
st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2;
st->transfer_size = bitmap_weight(scan_mask,
iio_get_masklength(indio_dev)) * 2;
switch (st->id) {
case ad7992:

View File

@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
@ -104,7 +105,30 @@
#define AD9467_DEF_OUTPUT_MODE 0x08
#define AD9467_REG_VREF_MASK 0x0F
/*
* Analog Devices AD9643 14-Bit, 170/210/250 MSPS ADC
*/
#define CHIPID_AD9643 0x82
#define AD9643_REG_VREF_MASK 0x1F
/*
* Analog Devices AD9652 16-bit 310 MSPS ADC
*/
#define CHIPID_AD9652 0xC1
#define AD9652_REG_VREF_MASK 0xC0
/*
* Analog Devices AD9649 14-bit 20/40/65/80 MSPS ADC
*/
#define CHIPID_AD9649 0x6F
#define AD9649_TEST_POINTS 8
#define AD9647_MAX_TEST_POINTS 32
#define AD9467_CAN_INVERT(st) \
(!(st)->info->has_dco || (st)->info->has_dco_invert)
struct ad9467_chip_info {
const char *name;
@ -113,12 +137,23 @@ struct ad9467_chip_info {
unsigned int num_channels;
const unsigned int (*scale_table)[2];
int num_scales;
unsigned long test_mask;
unsigned int test_mask_len;
unsigned long max_rate;
unsigned int default_output_mode;
unsigned int vref_mask;
unsigned int num_lanes;
unsigned int dco_en;
unsigned int test_points;
/* data clock output */
bool has_dco;
bool has_dco_invert;
};
struct ad9467_chan_test_mode {
struct ad9467_state *st;
unsigned int idx;
u8 mode;
};
struct ad9467_state {
@ -126,6 +161,8 @@ struct ad9467_state {
struct iio_backend *back;
struct spi_device *spi;
struct clk *clk;
/* used for debugfs */
struct ad9467_chan_test_mode *chan_test;
unsigned int output_mode;
unsigned int (*scales)[2];
/*
@ -138,6 +175,8 @@ struct ad9467_state {
* at the io delay control section.
*/
DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2);
/* number of bits of the map */
unsigned int calib_map_size;
struct gpio_desc *pwrdown_gpio;
/* ensure consistent state obtained on multiple related accesses */
struct mutex lock;
@ -211,6 +250,24 @@ static const unsigned int ad9467_scale_table[][2] = {
{2300, 8}, {2400, 9}, {2500, 10},
};
static const unsigned int ad9643_scale_table[][2] = {
{2087, 0x0F}, {2065, 0x0E}, {2042, 0x0D}, {2020, 0x0C}, {1997, 0x0B},
{1975, 0x0A}, {1952, 0x09}, {1930, 0x08}, {1907, 0x07}, {1885, 0x06},
{1862, 0x05}, {1840, 0x04}, {1817, 0x03}, {1795, 0x02}, {1772, 0x01},
{1750, 0x00}, {1727, 0x1F}, {1704, 0x1E}, {1681, 0x1D}, {1658, 0x1C},
{1635, 0x1B}, {1612, 0x1A}, {1589, 0x19}, {1567, 0x18}, {1544, 0x17},
{1521, 0x16}, {1498, 0x15}, {1475, 0x14}, {1452, 0x13}, {1429, 0x12},
{1406, 0x11}, {1383, 0x10},
};
static const unsigned int ad9649_scale_table[][2] = {
{2000, 0},
};
static const unsigned int ad9652_scale_table[][2] = {
{1250, 0}, {1125, 1}, {1200, 2}, {1250, 3}, {1000, 5},
};
static void __ad9467_get_scale(struct ad9467_state *st, int index,
unsigned int *val, unsigned int *val2)
{
@ -224,14 +281,14 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
*val2 = tmp % 1000000;
}
#define AD9467_CHAN(_chan, _si, _bits, _sign) \
#define AD9467_CHAN(_chan, avai_mask, _si, _bits, _sign) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = avai_mask, \
.scan_index = _si, \
.scan_type = { \
.sign = _sign, \
@ -241,11 +298,42 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
}
static const struct iio_chan_spec ad9434_channels[] = {
AD9467_CHAN(0, 0, 12, 's'),
AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 12, 's'),
};
static const struct iio_chan_spec ad9467_channels[] = {
AD9467_CHAN(0, 0, 16, 's'),
AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'),
};
static const struct iio_chan_spec ad9643_channels[] = {
AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 14, 's'),
AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 14, 's'),
};
static const struct iio_chan_spec ad9649_channels[] = {
AD9467_CHAN(0, 0, 0, 14, 's'),
};
static const struct iio_chan_spec ad9652_channels[] = {
AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'),
AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 16, 's'),
};
static const char * const ad9467_test_modes[] = {
[AN877_ADC_TESTMODE_OFF] = "off",
[AN877_ADC_TESTMODE_MIDSCALE_SHORT] = "midscale_short",
[AN877_ADC_TESTMODE_POS_FULLSCALE] = "pos_fullscale",
[AN877_ADC_TESTMODE_NEG_FULLSCALE] = "neg_fullscale",
[AN877_ADC_TESTMODE_ALT_CHECKERBOARD] = "checkerboard",
[AN877_ADC_TESTMODE_PN23_SEQ] = "prbs23",
[AN877_ADC_TESTMODE_PN9_SEQ] = "prbs9",
[AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE] = "one_zero_toggle",
[AN877_ADC_TESTMODE_USER] = "user",
[AN877_ADC_TESTMODE_BIT_TOGGLE] = "bit_toggle",
[AN877_ADC_TESTMODE_SYNC] = "sync",
[AN877_ADC_TESTMODE_ONE_BIT_HIGH] = "one_bit_high",
[AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY] = "mixed_bit_frequency",
[AN877_ADC_TESTMODE_RAMP] = "ramp",
};
static const struct ad9467_chip_info ad9467_chip_tbl = {
@ -256,6 +344,10 @@ static const struct ad9467_chip_info ad9467_chip_tbl = {
.num_scales = ARRAY_SIZE(ad9467_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
.test_points = AD9647_MAX_TEST_POINTS,
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.default_output_mode = AD9467_DEF_OUTPUT_MODE,
.vref_mask = AD9467_REG_VREF_MASK,
.num_lanes = 8,
@ -269,6 +361,9 @@ static const struct ad9467_chip_info ad9434_chip_tbl = {
.num_scales = ARRAY_SIZE(ad9434_scale_table),
.channels = ad9434_channels,
.num_channels = ARRAY_SIZE(ad9434_channels),
.test_points = AD9647_MAX_TEST_POINTS,
.test_mask = GENMASK(AN877_ADC_TESTMODE_USER, AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_USER + 1,
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
.num_lanes = 6,
@ -282,17 +377,78 @@ static const struct ad9467_chip_info ad9265_chip_tbl = {
.num_scales = ARRAY_SIZE(ad9265_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
.test_points = AD9647_MAX_TEST_POINTS,
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.default_output_mode = AD9265_DEF_OUTPUT_MODE,
.vref_mask = AD9265_REG_VREF_MASK,
.has_dco = true,
.has_dco_invert = true,
};
static const struct ad9467_chip_info ad9643_chip_tbl = {
.name = "ad9643",
.id = CHIPID_AD9643,
.max_rate = 250000000UL,
.scale_table = ad9643_scale_table,
.num_scales = ARRAY_SIZE(ad9643_scale_table),
.channels = ad9643_channels,
.num_channels = ARRAY_SIZE(ad9643_channels),
.test_points = AD9647_MAX_TEST_POINTS,
.test_mask = BIT(AN877_ADC_TESTMODE_RAMP) |
GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_RAMP + 1,
.vref_mask = AD9643_REG_VREF_MASK,
.has_dco = true,
.has_dco_invert = true,
.dco_en = AN877_ADC_DCO_DELAY_ENABLE,
};
static const struct ad9467_chip_info ad9649_chip_tbl = {
.name = "ad9649",
.id = CHIPID_AD9649,
.max_rate = 80000000UL,
.scale_table = ad9649_scale_table,
.num_scales = ARRAY_SIZE(ad9649_scale_table),
.channels = ad9649_channels,
.num_channels = ARRAY_SIZE(ad9649_channels),
.test_points = AD9649_TEST_POINTS,
.test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1,
.has_dco = true,
.has_dco_invert = true,
.dco_en = AN877_ADC_DCO_DELAY_ENABLE,
};
static const struct ad9467_chip_info ad9652_chip_tbl = {
.name = "ad9652",
.id = CHIPID_AD9652,
.max_rate = 310000000UL,
.scale_table = ad9652_scale_table,
.num_scales = ARRAY_SIZE(ad9652_scale_table),
.channels = ad9652_channels,
.num_channels = ARRAY_SIZE(ad9652_channels),
.test_points = AD9647_MAX_TEST_POINTS,
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.vref_mask = AD9652_REG_VREF_MASK,
.has_dco = true,
};
static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
{
const struct ad9467_chip_info *info = st->info;
unsigned int i, vref_val;
unsigned int vref_val;
unsigned int i = 0;
int ret;
/* nothing to read if we only have one possible scale */
if (info->num_scales == 1)
goto out_get_scale;
ret = ad9467_spi_read(st, AN877_ADC_REG_VREF);
if (ret < 0)
return ret;
@ -307,6 +463,7 @@ static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
if (i == info->num_scales)
return -ERANGE;
out_get_scale:
__ad9467_get_scale(st, i, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
@ -321,6 +478,8 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
if (val != 0)
return -EINVAL;
if (info->num_scales == 1)
return -EOPNOTSUPP;
for (i = 0; i < info->num_scales; i++) {
__ad9467_get_scale(st, i, &scale_val[0], &scale_val[1]);
@ -352,40 +511,96 @@ static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode)
AN877_ADC_TRANSFER_SYNC);
}
static int ad9647_calibrate_prepare(struct ad9467_state *st)
static int ad9467_testmode_set(struct ad9467_state *st, unsigned int chan,
unsigned int test_mode)
{
int ret;
if (st->info->num_channels > 1) {
/* so that the test mode is only applied to one channel */
ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX, BIT(chan));
if (ret)
return ret;
}
ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, test_mode);
if (ret)
return ret;
if (st->info->num_channels > 1) {
/* go to default state where all channels get write commands */
ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX,
GENMASK(st->info->num_channels - 1, 0));
if (ret)
return ret;
}
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
}
static int ad9467_backend_testmode_on(struct ad9467_state *st,
unsigned int chan,
enum iio_backend_test_pattern pattern)
{
struct iio_backend_data_fmt data = {
.enable = false,
};
unsigned int c;
int ret;
ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO,
AN877_ADC_TESTMODE_PN9_SEQ);
ret = iio_backend_data_format_set(st->back, chan, &data);
if (ret)
return ret;
ret = ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
ret = iio_backend_test_pattern_set(st->back, chan, pattern);
if (ret)
return ret;
return iio_backend_chan_enable(st->back, chan);
}
static int ad9467_backend_testmode_off(struct ad9467_state *st,
unsigned int chan)
{
struct iio_backend_data_fmt data = {
.enable = true,
.sign_extend = true,
};
int ret;
ret = iio_backend_chan_disable(st->back, chan);
if (ret)
return ret;
ret = iio_backend_test_pattern_set(st->back, chan,
IIO_BACKEND_NO_TEST_PATTERN);
if (ret)
return ret;
return iio_backend_data_format_set(st->back, chan, &data);
}
static int ad9647_calibrate_prepare(struct ad9467_state *st)
{
unsigned int c;
int ret;
ret = ad9467_outputmode_set(st, st->info->default_output_mode);
if (ret)
return ret;
for (c = 0; c < st->info->num_channels; c++) {
ret = iio_backend_data_format_set(st->back, c, &data);
ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_PN9_SEQ);
if (ret)
return ret;
ret = ad9467_backend_testmode_on(st, c,
IIO_BACKEND_ADI_PRBS_9A);
if (ret)
return ret;
}
ret = iio_backend_test_pattern_set(st->back, 0,
IIO_BACKEND_ADI_PRBS_9A);
if (ret)
return ret;
return iio_backend_chan_enable(st->back, 0);
return 0;
}
static int ad9647_calibrate_polarity_set(struct ad9467_state *st,
@ -442,7 +657,7 @@ static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val)
if (st->info->has_dco) {
ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_DELAY,
val);
val | st->info->dco_en);
if (ret)
return ret;
@ -461,57 +676,38 @@ static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val)
static int ad9647_calibrate_stop(struct ad9467_state *st)
{
struct iio_backend_data_fmt data = {
.sign_extend = true,
.enable = true,
};
unsigned int c, mode;
int ret;
ret = iio_backend_chan_disable(st->back, 0);
if (ret)
return ret;
ret = iio_backend_test_pattern_set(st->back, 0,
IIO_BACKEND_NO_TEST_PATTERN);
if (ret)
return ret;
for (c = 0; c < st->info->num_channels; c++) {
ret = iio_backend_data_format_set(st->back, c, &data);
ret = ad9467_backend_testmode_off(st, c);
if (ret)
return ret;
ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_OFF);
if (ret)
return ret;
}
mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
ret = ad9467_outputmode_set(st, mode);
if (ret)
return ret;
ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO,
AN877_ADC_TESTMODE_OFF);
if (ret)
return ret;
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
return ad9467_outputmode_set(st, mode);
}
static int ad9467_calibrate(struct ad9467_state *st)
{
unsigned int point, val, inv_val, cnt, inv_cnt = 0;
unsigned int point, val, inv_val, cnt, inv_cnt = 0, c;
/*
* Half of the bitmap is for the inverted signal. The number of test
* points is the same though...
*/
unsigned int test_points = AD9647_MAX_TEST_POINTS;
unsigned int test_points = st->info->test_points;
unsigned long sample_rate = clk_get_rate(st->clk);
struct device *dev = &st->spi->dev;
bool invert = false, stat;
int ret;
/* all points invalid */
bitmap_fill(st->calib_map, BITS_PER_TYPE(st->calib_map));
bitmap_fill(st->calib_map, st->calib_map_size);
ret = ad9647_calibrate_prepare(st);
if (ret)
@ -521,16 +717,31 @@ retune:
if (ret)
return ret;
for (point = 0; point < test_points; point++) {
for (point = 0; point < st->info->test_points; point++) {
ret = ad9467_calibrate_apply(st, point);
if (ret)
return ret;
ret = iio_backend_chan_status(st->back, 0, &stat);
if (ret)
return ret;
for (c = 0; c < st->info->num_channels; c++) {
ret = iio_backend_chan_status(st->back, c, &stat);
if (ret)
return ret;
__assign_bit(point + invert * test_points, st->calib_map, stat);
/*
* A point is considered valid if all channels report no
* error. If one reports an error, then we consider the
* point as invalid and we can break the loop right away.
*/
if (stat) {
dev_dbg(dev, "Invalid point(%u, inv:%u) for CH:%u\n",
point, invert, c);
break;
}
if (c == st->info->num_channels - 1)
__clear_bit(point + invert * test_points,
st->calib_map);
}
}
if (!invert) {
@ -541,8 +752,13 @@ retune:
* a row.
*/
if (cnt < 3) {
invert = true;
goto retune;
if (AD9467_CAN_INVERT(st)) {
invert = true;
goto retune;
}
if (!cnt)
return -EIO;
}
} else {
inv_cnt = ad9467_find_optimal_point(st->calib_map, test_points,
@ -679,7 +895,7 @@ static int ad9467_update_scan_mode(struct iio_dev *indio_dev,
return 0;
}
static const struct iio_info ad9467_info = {
static struct iio_info ad9467_info = {
.read_raw = ad9467_read_raw,
.write_raw = ad9467_write_raw,
.update_scan_mode = ad9467_update_scan_mode,
@ -762,12 +978,134 @@ static int ad9467_iio_backend_get(struct ad9467_state *st)
return -ENODEV;
}
static int ad9467_test_mode_available_show(struct seq_file *s, void *ignored)
{
struct ad9467_state *st = s->private;
unsigned int bit;
for_each_set_bit(bit, &st->info->test_mask, st->info->test_mask_len)
seq_printf(s, "%s\n", ad9467_test_modes[bit]);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ad9467_test_mode_available);
static ssize_t ad9467_chan_test_mode_read(struct file *file,
char __user *userbuf, size_t count,
loff_t *ppos)
{
struct ad9467_chan_test_mode *chan = file->private_data;
struct ad9467_state *st = chan->st;
char buf[128] = {0};
size_t len;
int ret;
if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ ||
chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) {
len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t",
ad9467_test_modes[chan->mode]);
ret = iio_backend_debugfs_print_chan_status(st->back, chan->idx,
buf + len,
sizeof(buf) - len);
if (ret < 0)
return ret;
len += ret;
} else if (chan->mode == AN877_ADC_TESTMODE_OFF) {
len = scnprintf(buf, sizeof(buf), "No test Running...\n");
} else {
len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test on CH:%u\n",
ad9467_test_modes[chan->mode], chan->idx);
}
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
static ssize_t ad9467_chan_test_mode_write(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ad9467_chan_test_mode *chan = file->private_data;
struct ad9467_state *st = chan->st;
char test_mode[32] = {0};
unsigned int mode;
int ret;
ret = simple_write_to_buffer(test_mode, sizeof(test_mode) - 1, ppos,
userbuf, count);
if (ret < 0)
return ret;
for_each_set_bit(mode, &st->info->test_mask, st->info->test_mask_len) {
if (sysfs_streq(test_mode, ad9467_test_modes[mode]))
break;
}
if (mode == st->info->test_mask_len)
return -EINVAL;
guard(mutex)(&st->lock);
if (mode == AN877_ADC_TESTMODE_OFF) {
unsigned int out_mode;
if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ ||
chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) {
ret = ad9467_backend_testmode_off(st, chan->idx);
if (ret)
return ret;
}
ret = ad9467_testmode_set(st, chan->idx, mode);
if (ret)
return ret;
out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
ret = ad9467_outputmode_set(st, out_mode);
if (ret)
return ret;
} else {
ret = ad9467_outputmode_set(st, st->info->default_output_mode);
if (ret)
return ret;
ret = ad9467_testmode_set(st, chan->idx, mode);
if (ret)
return ret;
/* some patterns have a backend matching monitoring block */
if (mode == AN877_ADC_TESTMODE_PN9_SEQ) {
ret = ad9467_backend_testmode_on(st, chan->idx,
IIO_BACKEND_ADI_PRBS_9A);
if (ret)
return ret;
} else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) {
ret = ad9467_backend_testmode_on(st, chan->idx,
IIO_BACKEND_ADI_PRBS_23A);
if (ret)
return ret;
}
}
chan->mode = mode;
return count;
}
static const struct file_operations ad9467_chan_test_mode_fops = {
.open = simple_open,
.read = ad9467_chan_test_mode_read,
.write = ad9467_chan_test_mode_write,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
static ssize_t ad9467_dump_calib_table(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ad9467_state *st = file->private_data;
unsigned int bit, size = BITS_PER_TYPE(st->calib_map);
unsigned int bit;
/* +2 for the newline and +1 for the string termination */
unsigned char map[AD9647_MAX_TEST_POINTS * 2 + 3];
ssize_t len = 0;
@ -776,8 +1114,8 @@ static ssize_t ad9467_dump_calib_table(struct file *file,
if (*ppos)
goto out_read;
for (bit = 0; bit < size; bit++) {
if (bit == size / 2)
for (bit = 0; bit < st->calib_map_size; bit++) {
if (AD9467_CAN_INVERT(st) && bit == st->calib_map_size / 2)
len += scnprintf(map + len, sizeof(map) - len, "\n");
len += scnprintf(map + len, sizeof(map) - len, "%c",
@ -800,12 +1138,33 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev)
{
struct dentry *d = iio_get_debugfs_dentry(indio_dev);
struct ad9467_state *st = iio_priv(indio_dev);
char attr_name[32];
unsigned int chan;
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return;
st->chan_test = devm_kcalloc(&st->spi->dev, st->info->num_channels,
sizeof(*st->chan_test), GFP_KERNEL);
if (!st->chan_test)
return;
debugfs_create_file("calibration_table_dump", 0400, d, st,
&ad9467_calib_table_fops);
for (chan = 0; chan < st->info->num_channels; chan++) {
snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode",
chan);
st->chan_test[chan].idx = chan;
st->chan_test[chan].st = st;
debugfs_create_file(attr_name, 0600, d, &st->chan_test[chan],
&ad9467_chan_test_mode_fops);
}
debugfs_create_file("in_voltage_test_mode_available", 0400, d, st,
&ad9467_test_mode_available_fops);
iio_backend_debugfs_add(st->back, indio_dev);
}
static int ad9467_probe(struct spi_device *spi)
@ -826,6 +1185,10 @@ static int ad9467_probe(struct spi_device *spi)
if (!st->info)
return -ENODEV;
st->calib_map_size = st->info->test_points;
if (AD9467_CAN_INVERT(st))
st->calib_map_size *= 2;
st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk");
if (IS_ERR(st->clk))
return PTR_ERR(st->clk);
@ -850,6 +1213,8 @@ static int ad9467_probe(struct spi_device *spi)
return -ENODEV;
}
if (st->info->num_scales > 1)
ad9467_info.read_avail = ad9467_read_avail;
indio_dev->name = st->info->name;
indio_dev->channels = st->info->channels;
indio_dev->num_channels = st->info->num_channels;
@ -884,6 +1249,9 @@ static const struct of_device_id ad9467_of_match[] = {
{ .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, },
{ .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, },
{ .compatible = "adi,ad9643", .data = &ad9643_chip_tbl, },
{ .compatible = "adi,ad9649", .data = &ad9649_chip_tbl, },
{ .compatible = "adi,ad9652", .data = &ad9652_chip_tbl, },
{}
};
MODULE_DEVICE_TABLE(of, ad9467_of_match);
@ -892,6 +1260,9 @@ static const struct spi_device_id ad9467_ids[] = {
{ "ad9265", (kernel_ulong_t)&ad9265_chip_tbl },
{ "ad9434", (kernel_ulong_t)&ad9434_chip_tbl },
{ "ad9467", (kernel_ulong_t)&ad9467_chip_tbl },
{ "ad9643", (kernel_ulong_t)&ad9643_chip_tbl },
{ "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, },
{ "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, },
{}
};
MODULE_DEVICE_TABLE(spi, ad9467_ids);

View File

@ -351,7 +351,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
if (sigma_delta->num_slots == 1) {
channel = find_first_bit(indio_dev->active_scan_mask,
indio_dev->masklength);
iio_get_masklength(indio_dev));
ret = ad_sigma_delta_set_channel(sigma_delta,
indio_dev->channels[channel].address);
if (ret)
@ -364,7 +364,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
* implementation is mandatory.
*/
slot = 0;
for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, i) {
sigma_delta->slots[slot] = indio_dev->channels[i].address;
slot++;
}
@ -526,7 +526,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
return bitmap_weight(mask, indio_dev->masklength) <= sigma_delta->num_slots;
return bitmap_weight(mask, iio_get_masklength(indio_dev)) <= sigma_delta->num_slots;
}
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {

View File

@ -61,6 +61,10 @@
#define ADI_AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40)
#define ADI_AXI_ADC_CHAN_STAT_PN_MASK GENMASK(2, 1)
/* out of sync */
#define ADI_AXI_ADC_CHAN_STAT_PN_OOS BIT(1)
/* spurious out of sync */
#define ADI_AXI_ADC_CHAN_STAT_PN_ERR BIT(2)
#define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40)
#define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16)
@ -199,17 +203,19 @@ static int axi_adc_test_pattern_set(struct iio_backend *back,
return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan),
ADI_AXI_ADC_CHAN_PN_SEL_MASK,
FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 0));
case IIO_BACKEND_ADI_PRBS_23A:
return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan),
ADI_AXI_ADC_CHAN_PN_SEL_MASK,
FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 1));
default:
return -EINVAL;
}
}
static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
bool *error)
static int axi_adc_read_chan_status(struct adi_axi_adc_state *st, unsigned int chan,
unsigned int *status)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
int ret;
u32 val;
guard(mutex)(&st->lock);
/* reset test bits by setting them */
@ -221,7 +227,18 @@ static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
/* let's give enough time to validate or erroring the incoming pattern */
fsleep(1000);
ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), &val);
return regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan),
status);
}
static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
bool *error)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
u32 val;
int ret;
ret = axi_adc_read_chan_status(st, chan, &val);
if (ret)
return ret;
@ -233,6 +250,30 @@ static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
return 0;
}
static int axi_adc_debugfs_print_chan_status(struct iio_backend *back,
unsigned int chan, char *buf,
size_t len)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
u32 val;
int ret;
ret = axi_adc_read_chan_status(st, chan, &val);
if (ret)
return ret;
/*
* PN_ERR is cleared in case out of sync is set. Hence, no point in
* checking both bits.
*/
if (val & ADI_AXI_ADC_CHAN_STAT_PN_OOS)
return scnprintf(buf, len, "CH%u: Out of Sync.\n", chan);
if (val & ADI_AXI_ADC_CHAN_STAT_PN_ERR)
return scnprintf(buf, len, "CH%u: Spurious Out of Sync.\n", chan);
return scnprintf(buf, len, "CH%u: OK.\n", chan);
}
static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
@ -267,13 +308,24 @@ static void axi_adc_free_buffer(struct iio_backend *back,
iio_dmaengine_buffer_free(buffer);
}
static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_write(st->regmap, reg, writeval);
}
static const struct regmap_config axi_adc_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
.reg_stride = 4,
};
static const struct iio_backend_ops adi_axi_adc_generic = {
static const struct iio_backend_ops adi_axi_adc_ops = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
.data_format_set = axi_adc_data_format_set,
@ -285,6 +337,13 @@ static const struct iio_backend_ops adi_axi_adc_generic = {
.iodelay_set = axi_adc_iodelays_set,
.test_pattern_set = axi_adc_test_pattern_set,
.chan_status = axi_adc_chan_status,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
};
static const struct iio_backend_info adi_axi_adc_generic = {
.name = "axi-adc",
.ops = &adi_axi_adc_ops,
};
static int adi_axi_adc_probe(struct platform_device *pdev)

View File

@ -555,8 +555,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
NULL)) {
if (of_property_present(data->dev->of_node, "aspeed,battery-sensing")) {
if (data->model_data->bat_sense_sup) {
data->battery_sensing = 1;
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &

View File

@ -7,6 +7,7 @@
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
@ -268,9 +269,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
struct iio_chan_spec const *chan;
int i, j = 0;
for (i = 0; i < idev->masklength; i++) {
if (!test_bit(i, idev->active_scan_mask))
continue;
iio_for_each_active_channel(idev, i) {
chan = idev->channels + i;
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
j++;
@ -543,22 +542,18 @@ static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
int i;
for (i = 0; i < st->caps->trigger_number; i++) {
char *name = kasprintf(GFP_KERNEL,
"%s-dev%d-%s",
idev->name,
iio_device_id(idev),
triggers[i].name);
char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s-dev%d-%s",
idev->name,
iio_device_id(idev),
triggers[i].name);
if (!name)
return -ENOMEM;
if (strcmp(trigger_name, name) == 0) {
kfree(name);
if (triggers[i].value == 0)
return -EINVAL;
return triggers[i].value;
}
kfree(name);
}
return -EINVAL;

View File

@ -157,9 +157,7 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
i = 0;
sample_invalid = false;
for_each_set_bit(scan_idx, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, scan_idx) {
channel = indio_dev->channels[scan_idx].channel;
cc10001_adc_start(adc_dev, channel);

View File

@ -108,7 +108,7 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2)
dln2->demux_count = 0;
/* Optimize all 8-channels case */
if (indio_dev->masklength &&
if (iio_get_masklength(indio_dev) &&
(*indio_dev->active_scan_mask & 0xff) == 0xff) {
dln2_adc_add_demux(dln2, 0, 0, 16);
dln2->ts_pad_offset = 0;
@ -117,9 +117,7 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2)
}
/* Build demux table from fixed 8-channels to active_scan_mask */
for_each_set_bit(out_ind,
indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, out_ind) {
/* Handle timestamp separately */
if (out_ind == DLN2_ADC_MAX_CHANNELS)
break;
@ -541,7 +539,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
/* Assign trigger channel based on first enabled channel */
trigger_chan = find_first_bit(indio_dev->active_scan_mask,
indio_dev->masklength);
iio_get_masklength(indio_dev));
if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
dln2->trigger_chan = trigger_chan;
ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,

View File

@ -363,10 +363,7 @@ static irqreturn_t hx711_trigger(int irq, void *p)
memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer));
for (i = 0; i < indio_dev->masklength; i++) {
if (!test_bit(i, indio_dev->active_scan_mask))
continue;
iio_for_each_active_channel(indio_dev, i) {
hx711_data->buffer[j] = hx711_reset_read(hx711_data,
indio_dev->channels[i].channel);
j++;

View File

@ -755,8 +755,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
* Single register reads: bulk_read will not work with ina226/219
* as there is no auto-increment of the register pointer.
*/
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
unsigned int val;
ret = regmap_read(chip->regmap,

View File

@ -174,8 +174,7 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p)
mutex_lock(&adc->lock);
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = max1118_read(indio_dev, scan_chan->channel);

View File

@ -13,6 +13,7 @@
*/
#include <linux/interrupt.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
@ -818,7 +819,6 @@ static int max1363_read_event_config(struct iio_dev *indio_dev,
static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
{
u8 *tx_buf;
int ret, i = 3, j;
unsigned long numelements;
int len;
@ -850,11 +850,10 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
}
numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS);
len = 3 * numelements + 3;
tx_buf = kmalloc(len, GFP_KERNEL);
if (!tx_buf) {
ret = -ENOMEM;
goto error_ret;
}
u8 *tx_buf __free(kfree) = kmalloc(len, GFP_KERNEL);
if (!tx_buf)
return -ENOMEM;
tx_buf[0] = st->configbyte;
tx_buf[1] = st->setupbyte;
tx_buf[2] = (st->monitor_speed << 1);
@ -893,11 +892,9 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
ret = st->send(st->client, tx_buf, len);
if (ret < 0)
goto error_ret;
if (ret != len) {
ret = -EIO;
goto error_ret;
}
return ret;
if (ret != len)
return -EIO;
/*
* Now that we hopefully have sensible thresholds in place it is
@ -910,18 +907,13 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0;
ret = st->send(st->client, tx_buf, 2);
if (ret < 0)
goto error_ret;
if (ret != 2) {
ret = -EIO;
goto error_ret;
}
ret = 0;
return ret;
if (ret != 2)
return -EIO;
st->monitor_on = true;
error_ret:
kfree(tx_buf);
return ret;
return 0;
}
/*

View File

@ -349,8 +349,6 @@ struct mcp3564_chip_info {
* struct mcp3564_state - working data for a ADC device
* @chip_info: chip specific data
* @spi: SPI device structure
* @vref: the regulator device used as a voltage reference in case
* external voltage reference is used
* @vref_mv: voltage reference value in miliVolts
* @lock: synchronize access to driver's state members
* @dev_addr: hardware device address
@ -369,7 +367,6 @@ struct mcp3564_chip_info {
struct mcp3564_state {
const struct mcp3564_chip_info *chip_info;
struct spi_device *spi;
struct regulator *vref;
unsigned short vref_mv;
struct mutex lock; /* Synchronize access to driver's state members */
u8 dev_addr;
@ -1085,11 +1082,6 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
return 0;
}
static void mcp3564_disable_reg(void *reg)
{
regulator_disable(reg);
}
static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc)
{
unsigned int pow = adc->chip_info->resolution - 1;
@ -1110,7 +1102,7 @@ static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc)
}
}
static int mcp3564_config(struct iio_dev *indio_dev)
static int mcp3564_config(struct iio_dev *indio_dev, bool *use_internal_vref_attr)
{
struct mcp3564_state *adc = iio_priv(indio_dev);
struct device *dev = &adc->spi->dev;
@ -1119,6 +1111,7 @@ static int mcp3564_config(struct iio_dev *indio_dev)
enum mcp3564_ids ids;
int ret = 0;
unsigned int tmp = 0x01;
bool internal_vref;
bool err = false;
/*
@ -1218,36 +1211,22 @@ static int mcp3564_config(struct iio_dev *indio_dev)
dev_dbg(dev, "Found %s chip\n", adc->chip_info->name);
adc->vref = devm_regulator_get_optional(dev, "vref");
if (IS_ERR(adc->vref)) {
if (PTR_ERR(adc->vref) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(adc->vref),
"failed to get regulator\n");
ret = devm_regulator_get_enable_read_voltage(dev, "vref");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "Failed to get vref voltage\n");
internal_vref = ret == -ENODEV;
adc->vref_mv = internal_vref ? MCP3564R_INT_VREF_MV : ret / MILLI;
*use_internal_vref_attr = internal_vref;
if (internal_vref) {
/* Check if chip has internal vref */
if (!adc->have_vref)
return dev_err_probe(dev, PTR_ERR(adc->vref),
"Unknown Vref\n");
adc->vref = NULL;
return dev_err_probe(dev, -ENODEV, "Unknown Vref\n");
dev_dbg(dev, "%s: Using internal Vref\n", __func__);
} else {
ret = regulator_enable(adc->vref);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, mcp3564_disable_reg,
adc->vref);
if (ret)
return ret;
dev_dbg(dev, "%s: Using External Vref\n", __func__);
ret = regulator_get_voltage(adc->vref);
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to read vref regulator\n");
adc->vref_mv = ret / MILLI;
}
ret = mcp3564_parse_fw_children(indio_dev);
@ -1350,10 +1329,8 @@ static int mcp3564_config(struct iio_dev *indio_dev)
tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK);
tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT;
if (!adc->vref) {
if (internal_vref)
tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1);
adc->vref_mv = MCP3564R_INT_VREF_MV;
}
ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg);
@ -1412,6 +1389,7 @@ static int mcp3564_probe(struct spi_device *spi)
int ret;
struct iio_dev *indio_dev;
struct mcp3564_state *adc;
bool use_internal_vref_attr;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
@ -1428,7 +1406,7 @@ static int mcp3564_probe(struct spi_device *spi)
* enable/disable certain channels
* change the sampling rate to the requested value
*/
ret = mcp3564_config(indio_dev);
ret = mcp3564_config(indio_dev, &use_internal_vref_attr);
if (ret)
return dev_err_probe(&spi->dev, ret,
"Can't configure MCP356X device\n");
@ -1440,7 +1418,7 @@ static int mcp3564_probe(struct spi_device *spi)
indio_dev->name = adc->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
if (!adc->vref)
if (use_internal_vref_attr)
indio_dev->info = &mcp3564r_info;
else
indio_dev->info = &mcp3564_info;

View File

@ -103,7 +103,7 @@ struct mcp3911_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
int (*config)(struct mcp3911 *adc);
int (*config)(struct mcp3911 *adc, bool external_vref);
int (*get_osr)(struct mcp3911 *adc, u32 *val);
int (*set_osr)(struct mcp3911 *adc, u32 val);
int (*enable_offset)(struct mcp3911 *adc, bool enable);
@ -115,7 +115,6 @@ struct mcp3911_chip_info {
struct mcp3911 {
struct spi_device *spi;
struct mutex lock;
struct regulator *vref;
struct clk *clki;
u32 dev_addr;
struct iio_trigger *trig;
@ -385,23 +384,11 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
}
}
static int mcp3911_calc_scale_table(struct mcp3911 *adc)
static int mcp3911_calc_scale_table(u32 vref_mv)
{
struct device *dev = &adc->spi->dev;
u32 ref = MCP3911_INT_VREF_MV;
u32 div;
int ret;
u64 tmp;
if (adc->vref) {
ret = regulator_get_voltage(adc->vref);
if (ret < 0) {
return dev_err_probe(dev, ret, "failed to get vref voltage\n");
}
ref = ret / 1000;
}
/*
* For 24-bit Conversion
* Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
@ -412,7 +399,7 @@ static int mcp3911_calc_scale_table(struct mcp3911 *adc)
*/
for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
div = 12582912 * BIT(i);
tmp = div_s64((s64)ref * 1000000000LL, div);
tmp = div_s64((s64)vref_mv * 1000000000LL, div);
mcp3911_scale_table[i][0] = 0;
mcp3911_scale_table[i][1] = tmp;
@ -523,7 +510,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
goto out;
}
for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index];
adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]);
@ -544,7 +531,7 @@ static const struct iio_info mcp3911_info = {
.write_raw_get_fmt = mcp3911_write_raw_get_fmt,
};
static int mcp3911_config(struct mcp3911 *adc)
static int mcp3911_config(struct mcp3911 *adc, bool external_vref)
{
struct device *dev = &adc->spi->dev;
u32 regval;
@ -555,7 +542,7 @@ static int mcp3911_config(struct mcp3911 *adc)
return ret;
regval &= ~MCP3911_CONFIG_VREFEXT;
if (adc->vref) {
if (external_vref) {
dev_dbg(dev, "use external voltage reference\n");
regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1);
} else {
@ -610,7 +597,7 @@ static int mcp3911_config(struct mcp3911 *adc)
return mcp3911_write(adc, MCP3911_REG_GAIN, regval, 1);
}
static int mcp3910_config(struct mcp3911 *adc)
static int mcp3910_config(struct mcp3911 *adc, bool external_vref)
{
struct device *dev = &adc->spi->dev;
u32 regval;
@ -621,7 +608,7 @@ static int mcp3910_config(struct mcp3911 *adc)
return ret;
regval &= ~MCP3910_CONFIG1_VREFEXT;
if (adc->vref) {
if (external_vref) {
dev_dbg(dev, "use external voltage reference\n");
regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 1);
} else {
@ -677,11 +664,6 @@ static int mcp3910_config(struct mcp3911 *adc)
return adc->chip->enable_offset(adc, 0);
}
static void mcp3911_cleanup_regulator(void *vref)
{
regulator_disable(vref);
}
static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct mcp3911 *adc = iio_trigger_get_drvdata(trig);
@ -704,6 +686,8 @@ static int mcp3911_probe(struct spi_device *spi)
struct device *dev = &spi->dev;
struct iio_dev *indio_dev;
struct mcp3911 *adc;
bool external_vref;
u32 vref_mv;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
@ -714,23 +698,12 @@ static int mcp3911_probe(struct spi_device *spi)
adc->spi = spi;
adc->chip = spi_get_device_match_data(spi);
adc->vref = devm_regulator_get_optional(dev, "vref");
if (IS_ERR(adc->vref)) {
if (PTR_ERR(adc->vref) == -ENODEV) {
adc->vref = NULL;
} else {
return dev_err_probe(dev, PTR_ERR(adc->vref), "failed to get regulator\n");
}
ret = devm_regulator_get_enable_read_voltage(dev, "vref");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to get vref voltage\n");
} else {
ret = regulator_enable(adc->vref);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, mcp3911_cleanup_regulator, adc->vref);
if (ret)
return ret;
}
external_vref = ret != -ENODEV;
vref_mv = external_vref ? ret / 1000 : MCP3911_INT_VREF_MV;
adc->clki = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(adc->clki)) {
@ -755,11 +728,11 @@ static int mcp3911_probe(struct spi_device *spi)
}
dev_dbg(dev, "use device address %i\n", adc->dev_addr);
ret = adc->chip->config(adc);
ret = adc->chip->config(adc, external_vref);
if (ret)
return ret;
ret = mcp3911_calc_scale_table(adc);
ret = mcp3911_calc_scale_table(vref_mv);
if (ret)
return ret;

View File

@ -268,7 +268,7 @@ static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
int i = 0, bit, val, ret;
memset(&data, 0, sizeof(data));
for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
ret = mt6360_adc_read_channel(mad, bit, &val);
if (ret < 0) {
dev_warn(&indio_dev->dev, "Failed to get channel %d conversion val\n", bit);

1261
drivers/iio/adc/pac1921.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -370,7 +370,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p)
mutex_lock(&info->lock);
for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) {
iio_for_each_active_channel(i_dev, i) {
const struct iio_chan_spec *chan = &i_dev->channels[i];
ret = rockchip_saradc_conversion(info, chan);

View File

@ -643,7 +643,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
pm_runtime_get_sync(dev);
for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, bit) {
unsigned int addr = rtq6056_channels[bit].address;
ret = regmap_read(priv->regmap, addr, &raw);

View File

@ -6,11 +6,14 @@
* Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
*/
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
static const struct iio_info iio_sd_mod_iio_info;
@ -24,7 +27,59 @@ static const struct iio_chan_spec iio_sd_mod_ch = {
},
};
static int iio_sd_mod_probe(struct platform_device *pdev)
struct iio_sd_backend_priv {
struct regulator *vref;
int vref_mv;
};
static int iio_sd_mod_enable(struct iio_backend *backend)
{
struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend);
if (priv->vref)
return regulator_enable(priv->vref);
return 0;
};
static void iio_sd_mod_disable(struct iio_backend *backend)
{
struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend);
if (priv->vref)
regulator_disable(priv->vref);
};
static int iio_sd_mod_read(struct iio_backend *backend, struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*val = priv->vref_mv;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
*val = 0;
return IIO_VAL_INT;
}
return -EOPNOTSUPP;
};
static const struct iio_backend_ops sd_backend_ops = {
.enable = iio_sd_mod_enable,
.disable = iio_sd_mod_disable,
.read_raw = iio_sd_mod_read,
};
static const struct iio_backend_info sd_backend_info = {
.name = "sd-modulator",
.ops = &sd_backend_ops,
};
static int iio_sd_mod_register(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *iio;
@ -45,6 +100,45 @@ static int iio_sd_mod_probe(struct platform_device *pdev)
return devm_iio_device_register(&pdev->dev, iio);
}
static int iio_sd_mod_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regulator *vref;
struct iio_sd_backend_priv *priv;
int ret;
/* If sd modulator is not defined as an IIO backend device, fallback to legacy */
if (!device_property_present(dev, "#io-backend-cells"))
return iio_sd_mod_register(pdev);
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/*
* Get regulator reference if any, but don't enable regulator right now.
* Rely on enable and disable callbacks to manage regulator power.
*/
vref = devm_regulator_get_optional(dev, "vref");
if (IS_ERR(vref)) {
if (PTR_ERR(vref) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(vref), "Failed to get vref\n");
} else {
/*
* Retrieve voltage right now, as regulator_get_voltage() provides it whatever
* the state of the regulator.
*/
ret = regulator_get_voltage(vref);
if (ret < 0)
return ret;
priv->vref = vref;
priv->vref_mv = ret / 1000;
}
return devm_iio_backend_register(&pdev->dev, &sd_backend_info, priv);
};
static const struct of_device_id sd_adc_of_match[] = {
{ .compatible = "sd-modulator" },
{ .compatible = "ads1201" },
@ -65,3 +159,4 @@ module_platform_driver(iio_sd_mod_adc);
MODULE_DESCRIPTION("Basic sigma delta modulator");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_BACKEND);

View File

@ -1261,7 +1261,7 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]);
stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]);
for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
for_each_set_bit(bit, scan_mask, iio_get_masklength(indio_dev)) {
chan = indio_dev->channels + bit;
/*
* Assign one channel per SQ entry in regular
@ -1619,7 +1619,7 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
adc->num_conv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev));
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
pm_runtime_mark_last_busy(dev);

View File

@ -9,6 +9,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
#include <linux/iio/backend.h>
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
@ -78,6 +79,7 @@ struct stm32_dfsdm_adc {
/* ADC specific */
unsigned int oversamp;
struct iio_hw_consumer *hwc;
struct iio_backend **backend;
struct completion completion;
u32 *buffer;
@ -666,6 +668,74 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
return 0;
}
static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm,
struct iio_dev *indio_dev,
struct iio_chan_spec *ch,
struct fwnode_handle *node)
{
struct stm32_dfsdm_channel *df_ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct iio_backend *backend;
const char *of_str;
int ret, val;
ret = fwnode_property_read_u32(node, "reg", &ch->channel);
if (ret < 0) {
dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
return ret;
}
if (ch->channel >= dfsdm->num_chs) {
dev_err(&indio_dev->dev, " Error bad channel number %d (max = %d)\n",
ch->channel, dfsdm->num_chs);
return -EINVAL;
}
ret = fwnode_property_read_string(node, "label", &ch->datasheet_name);
if (ret < 0) {
dev_err(&indio_dev->dev,
" Error parsing 'label' for idx %d\n", ch->channel);
return ret;
}
df_ch = &dfsdm->ch_list[ch->channel];
df_ch->id = ch->channel;
ret = fwnode_property_read_string(node, "st,adc-channel-type", &of_str);
if (!ret) {
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
if (val < 0)
return val;
} else {
val = 0;
}
df_ch->type = val;
ret = fwnode_property_read_string(node, "st,adc-channel-clk-src", &of_str);
if (!ret) {
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
if (val < 0)
return val;
} else {
val = 0;
}
df_ch->src = val;
ret = fwnode_property_read_u32(node, "st,adc-alt-channel", &df_ch->alt_si);
if (ret != -EINVAL)
df_ch->alt_si = 0;
if (adc->dev_data->type == DFSDM_IIO) {
backend = devm_iio_backend_fwnode_get(&indio_dev->dev, NULL, node);
if (IS_ERR(backend))
return dev_err_probe(&indio_dev->dev, PTR_ERR(backend),
"Failed to get backend\n");
adc->backend[ch->scan_index] = backend;
}
return 0;
}
static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
@ -987,7 +1057,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
adc->nconv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev));
adc->smask = *scan_mask;
dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
@ -998,6 +1068,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int i = 0;
int ret;
/* Reset adc buffer index */
@ -1009,6 +1080,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
return ret;
}
if (adc->backend) {
while (adc->backend[i]) {
ret = iio_backend_enable(adc->backend[i]);
if (ret < 0)
return ret;
i++;
}
}
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
goto err_stop_hwc;
@ -1041,6 +1121,7 @@ err_stop_hwc:
static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int i = 0;
stm32_dfsdm_stop_conv(indio_dev);
@ -1048,6 +1129,13 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
if (adc->backend) {
while (adc->backend[i]) {
iio_backend_disable(adc->backend[i]);
i++;
}
}
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);
@ -1220,14 +1308,25 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
u32 max = flo->max << (flo->lshift - chan->scan_type.shift);
int idx = chan->scan_index;
int ret;
if (flo->lshift < chan->scan_type.shift)
max = flo->max >> (chan->scan_type.shift - flo->lshift);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = iio_hw_consumer_enable(adc->hwc);
if (adc->hwc)
ret = iio_hw_consumer_enable(adc->hwc);
if (adc->backend)
ret = iio_backend_enable(adc->backend[idx]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
@ -1236,7 +1335,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
iio_hw_consumer_disable(adc->hwc);
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);
if (adc->backend)
iio_backend_disable(adc->backend[idx]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
@ -1256,6 +1358,50 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
*val = adc->sample_freq;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/*
* Scale is expressed in mV.
* When fast mode is disabled, actual resolution may be lower
* than 2^n, where n = realbits - 1.
* This leads to underestimating the input voltage.
* To compensate this deviation, the voltage reference can be
* corrected with a factor = realbits resolution / actual max
*/
if (adc->backend) {
ret = iio_backend_read_scale(adc->backend[idx], chan, val, NULL);
if (ret < 0)
return ret;
*val = div_u64((u64)*val * (u64)BIT(DFSDM_DATA_RES - 1), max);
*val2 = chan->scan_type.realbits;
if (chan->differential)
*val *= 2;
}
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
/*
* DFSDM output data are in the range [-2^n, 2^n],
* with n = realbits - 1.
* - Differential modulator:
* Offset correspond to SD modulator offset.
* - Single ended modulator:
* Input is in [0V, Vref] range,
* where 0V corresponds to -2^n, and Vref to 2^n.
* Add 2^n to offset. (i.e. middle of input range)
* offset = offset(sd) * vref / res(sd) * max / vref.
*/
if (adc->backend) {
ret = iio_backend_read_offset(adc->backend[idx], chan, val, NULL);
if (ret < 0)
return ret;
*val = div_u64((u64)max * *val, BIT(*val2 - 1));
if (!chan->differential)
*val += max;
}
return IIO_VAL_INT;
}
return -EINVAL;
@ -1362,15 +1508,18 @@ static int stm32_dfsdm_dma_request(struct device *dev,
return 0;
}
static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
struct iio_chan_spec *ch)
static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_chan_spec *ch,
struct fwnode_handle *child)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int ret;
ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
if (child)
ret = stm32_dfsdm_generic_channel_parse_of(adc->dfsdm, indio_dev, ch, child);
else /* Legacy binding */
ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
if (ret < 0)
return ret;
return dev_err_probe(&indio_dev->dev, ret, "Failed to parse channel\n");
ch->type = IIO_VOLTAGE;
ch->indexed = 1;
@ -1379,12 +1528,21 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
* IIO_CHAN_INFO_RAW: used to compute regular conversion
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
if (child) {
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET);
} else {
/* Legacy. Scaling not supported */
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
}
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ);
if (adc->dev_data->type == DFSDM_AUDIO) {
ch->ext_info = dfsdm_adc_audio_ext_info;
ch->scan_index = 0;
} else {
ch->scan_type.shift = 8;
}
@ -1396,20 +1554,67 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
&adc->dfsdm->ch_list[ch->channel]);
}
static int stm32_dfsdm_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels)
{
int num_ch = indio_dev->num_channels;
int chan_idx = 0;
int ret;
for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
channels[chan_idx].scan_index = chan_idx;
ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], NULL);
if (ret < 0)
return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n");
}
return 0;
}
static int stm32_dfsdm_generic_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels)
{
int chan_idx = 0, ret;
device_for_each_child_node_scoped(&indio_dev->dev, child) {
/* Skip DAI node in DFSDM audio nodes */
if (fwnode_property_present(child, "compatible"))
continue;
channels[chan_idx].scan_index = chan_idx;
ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], child);
if (ret < 0)
return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n");
chan_idx++;
}
return chan_idx;
}
static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
{
struct iio_chan_spec *ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct stm32_dfsdm_channel *d_ch;
int ret;
bool legacy = false;
int num_ch, ret;
/* If st,adc-channels is defined legacy binding is used. Else assume generic binding. */
num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels");
if (num_ch == 1)
legacy = true;
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;
ch->scan_index = 0;
indio_dev->num_channels = 1;
indio_dev->channels = ch;
if (legacy)
ret = stm32_dfsdm_chan_init(indio_dev, ch);
else
ret = stm32_dfsdm_generic_chan_init(indio_dev, ch);
ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
if (ret < 0) {
dev_err(&indio_dev->dev, "Channels init failed\n");
return ret;
@ -1420,9 +1625,6 @@ static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
adc->spi_freq = adc->dfsdm->spi_master_freq;
indio_dev->num_channels = 1;
indio_dev->channels = ch;
return stm32_dfsdm_dma_request(dev, indio_dev);
}
@ -1430,43 +1632,61 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
{
struct iio_chan_spec *ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int num_ch;
int ret, chan_idx;
int num_ch, ret;
bool legacy = false;
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
if (ret < 0)
return ret;
num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
"st,adc-channels");
if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) {
dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
return num_ch < 0 ? num_ch : -EINVAL;
num_ch = device_get_child_node_count(&indio_dev->dev);
if (!num_ch) {
/* No channels nodes found. Assume legacy binding */
num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels");
if (num_ch < 0) {
dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
return num_ch;
}
legacy = true;
}
/* Bind to SD modulator IIO device */
adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
if (IS_ERR(adc->hwc))
return -EPROBE_DEFER;
if (num_ch > adc->dfsdm->num_chs) {
dev_err(&indio_dev->dev, "Number of channel [%d] exceeds [%d]\n",
num_ch, adc->dfsdm->num_chs);
return -EINVAL;
}
indio_dev->num_channels = num_ch;
ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
GFP_KERNEL);
if (legacy) {
/* Bind to SD modulator IIO device. */
adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
if (IS_ERR(adc->hwc))
return dev_err_probe(&indio_dev->dev, -EPROBE_DEFER,
"waiting for SD modulator\n");
} else {
/* Generic binding. SD modulator IIO device not used. Use SD modulator backend. */
adc->hwc = NULL;
adc->backend = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*adc->backend),
GFP_KERNEL);
if (!adc->backend)
return -ENOMEM;
}
ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;
for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
ch[chan_idx].scan_index = chan_idx;
ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]);
if (ret < 0) {
dev_err(&indio_dev->dev, "Channels init failed\n");
return ret;
}
}
indio_dev->num_channels = num_ch;
indio_dev->channels = ch;
if (legacy)
ret = stm32_dfsdm_chan_init(indio_dev, ch);
else
ret = stm32_dfsdm_generic_chan_init(indio_dev, ch);
if (ret < 0)
return ret;
init_completion(&adc->completion);
/* Optionally request DMA */
@ -1677,3 +1897,4 @@ module_platform_driver(stm32_dfsdm_adc_driver);
MODULE_DESCRIPTION("STM32 sigma delta ADC");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_BACKEND);

View File

@ -211,8 +211,7 @@ static irqreturn_t adc0832_trigger_handler(int irq, void *p)
mutex_lock(&adc->lock);
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = adc0832_adc_conversion(adc, scan_chan->channel,

Some files were not shown because too many files have changed in this diff Show More