whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit 738b04fba18d35cd352b7b15afefb8a7b798648e
parent fe675d4d3c6b96710d481346821839b4a817c672
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Mon, 29 Oct 2018 10:38:10 -0700

Merge tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging/IIO driver updates from Greg KH:
 "Here is the big staging and IIO driver pull request for 4.20-rc1.

  There are lots of things here, we ended up adding more lines than
  removing, thanks to a large influx of Comedi National Instrument
  device support. Someday soon we need to get comedi out of staging...

  Other than the comedi drivers, the "big" things here are:

   - new iio drivers

   - delete dgnc driver (no one used it and no one had the hardware
     anymore)

   - vbox driver updates and fixes

   - erofs fixes

   - tons and tons of tiny checkpatch fixes for almost all staging
     drivers

  All of these have been in linux-next, with the last few happening a
  bit "late" due to them getting stuck on my laptop during travel to the
  Mantainers summit"

* tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (690 commits)
  staging: gasket: Fix sparse "incorrect type in assignment" warnings.
  staging: gasket: remove debug logs for callback invocation
  staging: gasket: remove debug logs in page table mapping calls
  staging: rtl8188eu: core: Use sizeof(*p) instead of sizeof(struct P) for memory allocation
  staging: ks7010: Remove extra blank line
  staging: gasket: Remove extra blank line
  staging: media: davinci_vpfe: Fix spelling mistake in enum
  staging: speakup: Add a pair of braces
  staging: wlan-ng: Replace long int with long
  staging: MAINTAINERS: remove obsolete IPX staging directory
  staging: MAINTAINERS: remove NCP filesystem entry
  staging: rtl8188eu: cleanup comparsions to false
  staging: gasket: Update device virtual address comment
  staging: gasket: sysfs: fix attribute release comment
  staging: gasket: apex: fix sysfs_show
  staging: gasket: page_table: simplify gasket_components_to_dev_address
  staging: gasket: page_table: fix comment in components_to_dev_address
  staging: gasket: page table: fixup error path allocating coherent mem
  staging: gasket: page_table: rearrange gasket_page_table_entry
  staging: gasket: page_table: remove unnecessary PTE status set to free
  ...

Diffstat:
MDocumentation/ABI/testing/sysfs-bus-iio | 2+-
ADocumentation/devicetree/bindings/iio/accel/adxl372.txt | 33+++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/iio/adc/mcp3911.txt | 30++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 81++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
MDocumentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt | 4++++
MDocumentation/devicetree/bindings/iio/dac/ad5758.txt | 5+++++
ADocumentation/devicetree/bindings/iio/dac/ltc1660.txt | 21+++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/imu/inv_mpu6050.txt | 1+
MDocumentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt | 1+
ADocumentation/devicetree/bindings/iio/light/bh1750.txt | 18++++++++++++++++++
ADocumentation/devicetree/bindings/iio/light/tsl2772.txt | 42++++++++++++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/iio/proximity/vl53l0x.txt | 12++++++++++++
MDocumentation/devicetree/bindings/trivial-devices.txt | 10----------
DDocumentation/filesystems/pohmelfs/design_notes.txt | 72------------------------------------------------------------------------
DDocumentation/filesystems/pohmelfs/info.txt | 99-------------------------------------------------------------------------------
DDocumentation/filesystems/pohmelfs/network_protocol.txt | 227-------------------------------------------------------------------------------
MMAINTAINERS | 52++++++++++++++++++++++++++++++++--------------------
Mdrivers/iio/accel/Kconfig | 27+++++++++++++++++++++++++++
Mdrivers/iio/accel/Makefile | 3+++
Mdrivers/iio/accel/adxl345_i2c.c | 5++++-
Adrivers/iio/accel/adxl372.c | 975+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/accel/adxl372.h | 17+++++++++++++++++
Adrivers/iio/accel/adxl372_i2c.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/accel/adxl372_spi.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/Kconfig | 30++++++++++++++++++++++++++++++
Mdrivers/iio/adc/Makefile | 2++
Mdrivers/iio/adc/ad7298.c | 2+-
Mdrivers/iio/adc/ad7476.c | 2+-
Mdrivers/iio/adc/ad7793.c | 2+-
Mdrivers/iio/adc/ad7887.c | 2+-
Mdrivers/iio/adc/ad7923.c | 2+-
Mdrivers/iio/adc/ad799x.c | 2+-
Mdrivers/iio/adc/at91_adc.c | 6+++++-
Mdrivers/iio/adc/envelope-detector.c | 5+----
Mdrivers/iio/adc/fsl-imx25-gcq.c | 6++++++
Mdrivers/iio/adc/max9611.c | 2+-
Adrivers/iio/adc/mcp3911.c | 363+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/meson_saradc.c | 70+++++++++++++++++++++++++++++++++++-----------------------------------
Mdrivers/iio/adc/qcom-pm8xxx-xoadc.c | 4++--
Adrivers/iio/adc/qcom-spmi-adc5.c | 793+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/qcom-vadc-common.c | 189++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mdrivers/iio/adc/qcom-vadc-common.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/rcar-gyroadc.c | 12++++++------
Mdrivers/iio/adc/sc27xx_adc.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mdrivers/iio/adc/ti-ads7950.c | 53+++++++++++++++++++++++++++++++----------------------
Mdrivers/iio/amplifiers/ad8366.c | 2+-
Mdrivers/iio/chemical/bme680.h | 19++++++++-----------
Mdrivers/iio/chemical/bme680_core.c | 154++++++++++++++++++++++++++++++-------------------------------------------------
Mdrivers/iio/dac/Kconfig | 10++++++++++
Mdrivers/iio/dac/Makefile | 1+
Mdrivers/iio/dac/ad5064.c | 53++++++++++++++++++++++++++++++++++++++---------------
Mdrivers/iio/dac/ad5446.c | 2+-
Mdrivers/iio/dac/ad5504.c | 2+-
Mdrivers/iio/dac/ad5686.c | 2+-
Mdrivers/iio/dac/ad5758.c | 26++++++++++++++++++++++++--
Mdrivers/iio/dac/ad5791.c | 2+-
Mdrivers/iio/dac/dpot-dac.c | 5+----
Adrivers/iio/dac/ltc1660.c | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/dac/max517.c | 11+++--------
Mdrivers/iio/dac/max5821.c | 11+++--------
Mdrivers/iio/dac/mcp4725.c | 12+++---------
Mdrivers/iio/dac/mcp4922.c | 11++++++++---
Mdrivers/iio/dac/ti-dac5571.c | 1+
Mdrivers/iio/frequency/ad9523.c | 2+-
Mdrivers/iio/frequency/adf4350.c | 2+-
Mdrivers/iio/health/max30102.c | 6++++--
Mdrivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2++
Mdrivers/iio/imu/st_lsm6dsx/Kconfig | 2+-
Mdrivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 18+++++++++++++++++-
Mdrivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdrivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++--
Mdrivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 5+++++
Mdrivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 5+++++
Mdrivers/iio/light/bh1750.c | 25++++++++++++++-----------
Mdrivers/iio/light/max44000.c | 1-
Mdrivers/iio/light/tsl2772.c | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdrivers/iio/magnetometer/hmc5843.h | 2+-
Mdrivers/iio/multiplexer/iio-mux.c | 5+----
Mdrivers/iio/potentiometer/max5481.c | 7++-----
Mdrivers/iio/potentiometer/mcp4018.c | 9+++------
Mdrivers/iio/potentiometer/mcp4531.c | 14++++----------
Mdrivers/iio/pressure/ms5611.h | 5+----
Mdrivers/iio/pressure/ms5611_core.c | 5+----
Mdrivers/iio/pressure/ms5611_i2c.c | 7+------
Mdrivers/iio/pressure/ms5611_spi.c | 7+------
Mdrivers/iio/proximity/Kconfig | 11+++++++++++
Mdrivers/iio/proximity/Makefile | 2++
Mdrivers/iio/proximity/isl29501.c | 12++----------
Adrivers/iio/proximity/vl53l0x-i2c.c | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/trigger/iio-trig-sysfs.c | 2+-
Mdrivers/staging/Kconfig | 2--
Mdrivers/staging/Makefile | 1-
Mdrivers/staging/android/ion/ion.h | 5-----
Mdrivers/staging/android/ion/ion_system_heap.c | 24------------------------
Mdrivers/staging/axis-fifo/axis-fifo.c | 24++++++++++++------------
Mdrivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c | 20++++++++++----------
Mdrivers/staging/comedi/Kconfig | 4++++
Mdrivers/staging/comedi/comedi.h | 174++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdrivers/staging/comedi/comedi_fops.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/comedi/comedidev.h | 14++++++++++++++
Mdrivers/staging/comedi/drivers.c | 19+++++++++++++++++++
Mdrivers/staging/comedi/drivers/Makefile | 28++++++++++++++++++++++++++++
Mdrivers/staging/comedi/drivers/comedi_test.c | 44++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/comedi/drivers/ni_660x.c | 363++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mdrivers/staging/comedi/drivers/ni_mio_common.c | 944+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mdrivers/staging/comedi/drivers/ni_pcidio.c | 13+++++++++++++
Mdrivers/staging/comedi/drivers/ni_pcimio.c | 21+++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routes.c | 523+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routes.h | 329+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/README | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes.h | 32++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/all.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c | 639+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c | 1418+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c | 1602+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6229.c | 1602+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6251.c | 1652+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6254.c | 1464+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6259.c | 1652+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6534.c | 290+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6602.c | 3378+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6713.c | 400+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6723.c | 400+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6733.c | 428+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c | 608+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c | 1432+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c | 1613+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c | 1655+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c | 428+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c | 1656+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c | 575+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c | 3083+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_route_values.c | 42++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_route_values.h | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_route_values/all.h | 37+++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_route_values/ni_660x.c | 650+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_route_values/ni_eseries.c | 602+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/ni_route_values/ni_mseries.c | 1752+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/.gitignore | 7+++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/Makefile | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/convert_c_to_py.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/convert_csv_to_c.py | 503+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/convert_py_to_csv.py | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/csv_collection.py | 40++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/make_blank_csv.py | 32++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/ni_routing/tools/ni_names.py | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/comedi/drivers/ni_stc.h | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdrivers/staging/comedi/drivers/ni_tio.c | 461+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mdrivers/staging/comedi/drivers/ni_tio.h | 42+++++++++++++++++++++++++++++++++++++++++-
Mdrivers/staging/comedi/drivers/ni_tio_internal.h | 2++
Mdrivers/staging/comedi/drivers/ni_tiocmd.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Adrivers/staging/comedi/drivers/tests/Makefile | 7+++++++
Adrivers/staging/comedi/drivers/tests/example_test.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/tests/ni_routes_test.c | 613+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/comedi/drivers/tests/unittest.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddrivers/staging/dgnc/Kconfig | 6------
Ddrivers/staging/dgnc/Makefile | 4----
Ddrivers/staging/dgnc/TODO | 6------
Ddrivers/staging/dgnc/dgnc_cls.c | 1135-------------------------------------------------------------------------------
Ddrivers/staging/dgnc/dgnc_cls.h | 67-------------------------------------------------------------------
Ddrivers/staging/dgnc/dgnc_driver.c | 404-------------------------------------------------------------------------------
Ddrivers/staging/dgnc/dgnc_driver.h | 345-------------------------------------------------------------------------------
Ddrivers/staging/dgnc/dgnc_tty.c | 2372-------------------------------------------------------------------------------
Ddrivers/staging/dgnc/dgnc_tty.h | 24------------------------
Ddrivers/staging/dgnc/digi.h | 128-------------------------------------------------------------------------------
Mdrivers/staging/emxx_udc/emxx_udc.c | 43++-----------------------------------------
Mdrivers/staging/erofs/Kconfig | 9+++++++++
Mdrivers/staging/erofs/data.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mdrivers/staging/erofs/dir.c | 15++++++++-------
Mdrivers/staging/erofs/erofs_fs.h | 11+++++++++++
Mdrivers/staging/erofs/include/trace/events/erofs.h | 20++++++++++++++++++--
Mdrivers/staging/erofs/inode.c | 50++++++++++++++++++++++++++++++++++++++++----------
Mdrivers/staging/erofs/internal.h | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mdrivers/staging/erofs/namei.c | 28++++++++++++++--------------
Mdrivers/staging/erofs/super.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mdrivers/staging/erofs/unzip_vle.c | 447++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdrivers/staging/erofs/unzip_vle.h | 12+-----------
Mdrivers/staging/erofs/unzip_vle_lz4.c | 69++++++++++++++++++++++++++++++++++-----------------------------------
Mdrivers/staging/erofs/utils.c | 2+-
Mdrivers/staging/erofs/xattr.c | 196++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mdrivers/staging/fbtft/fbtft.h | 58+++++++++++++++++++++++++++++-----------------------------
Mdrivers/staging/fsl-dpaa2/ethsw/ethsw.c | 6++----
Mdrivers/staging/gasket/Kconfig | 5+++--
Mdrivers/staging/gasket/apex_driver.c | 6+-----
Mdrivers/staging/gasket/gasket_core.c | 145++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdrivers/staging/gasket/gasket_core.h | 21+++++----------------
Mdrivers/staging/gasket/gasket_interrupt.c | 110+++++++++++++++++++++++++++----------------------------------------------------
Mdrivers/staging/gasket/gasket_interrupt.h | 24+-----------------------
Mdrivers/staging/gasket/gasket_page_table.c | 162+++++++++++++++++++++++++++++++++++--------------------------------------------
Mdrivers/staging/gasket/gasket_sysfs.h | 4++--
Mdrivers/staging/greybus/audio_codec.c | 1-
Mdrivers/staging/greybus/loopback.c | 8--------
Mdrivers/staging/greybus/tools/README.loopback | 2+-
Mdrivers/staging/greybus/tools/loopback_test.c | 2+-
Mdrivers/staging/iio/adc/Kconfig | 2+-
Mdrivers/staging/iio/adc/ad7192.c | 2+-
Mdrivers/staging/iio/adc/ad7280a.c | 2+-
Mdrivers/staging/iio/adc/ad7606.c | 42++++++++++++++++++++++++++++++++++--------
Mdrivers/staging/iio/adc/ad7606.h | 31++++++++++++++++++++++++++++---
Mdrivers/staging/iio/adc/ad7606_par.c | 5++++-
Mdrivers/staging/iio/adc/ad7606_spi.c | 3++-
Mdrivers/staging/iio/adc/ad7780.c | 2+-
Mdrivers/staging/iio/cdc/ad7746.c | 2+-
Mdrivers/staging/iio/frequency/ad9832.c | 2+-
Mdrivers/staging/iio/frequency/ad9834.c | 2+-
Mdrivers/staging/iio/impedance-analyzer/ad5933.c | 2+-
Mdrivers/staging/ks7010/ks_hostif.c | 5++---
Mdrivers/staging/media/davinci_vpfe/dm365_ipipe.c | 6+++---
Mdrivers/staging/media/davinci_vpfe/dm365_resizer.c | 2+-
Mdrivers/staging/media/davinci_vpfe/dm365_resizer.h | 2+-
Mdrivers/staging/media/imx/imx-media-dev.c | 15++++++++++-----
Mdrivers/staging/media/imx/imx-media-of.c | 4++--
Mdrivers/staging/most/cdev/cdev.c | 12++++++------
Mdrivers/staging/most/core.c | 49++++++++++++++++++++++++++++++++++---------------
Mdrivers/staging/most/net/net.c | 2+-
Mdrivers/staging/most/usb/usb.c | 55+++++++++++++++++++++++++++++++------------------------
Mdrivers/staging/most/video/video.c | 4++--
Mdrivers/staging/mt7621-dma/ralink-gdma.c | 1-
Mdrivers/staging/mt7621-eth/gsw_mt7621.c | 1-
Mdrivers/staging/mt7621-eth/mdio.c | 4++--
Mdrivers/staging/mt7621-eth/mtk_eth_soc.c | 1-
Mdrivers/staging/mt7621-mmc/dbg.c | 104+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/staging/mt7621-mmc/dbg.h | 100+++++++++++++++++++++----------------------------------------------------------
Mdrivers/staging/mt7621-mmc/sd.c | 708+++++++++++--------------------------------------------------------------------
Mdrivers/staging/mt7621-pci/pci-mt7621.c | 76+++++++++++++++++++++++++++++++++-------------------------------------------
Mdrivers/staging/octeon-usb/octeon-hcd.c | 58++++++++++++++++++++++++++++++----------------------------
Mdrivers/staging/olpc_dcon/Kconfig | 1+
Mdrivers/staging/olpc_dcon/olpc_dcon.c | 5+----
Mdrivers/staging/olpc_dcon/olpc_dcon_xo_1.c | 5+----
Mdrivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c | 5+----
Mdrivers/staging/pi433/rf69.c | 3+--
Mdrivers/staging/rtl8188eu/Makefile | 2+-
Mdrivers/staging/rtl8188eu/TODO | 2+-
Mdrivers/staging/rtl8188eu/core/rtw_ap.c | 25++++++++-----------------
Mdrivers/staging/rtl8188eu/core/rtw_cmd.c | 57++++++++++++++++++++++++++++-----------------------------
Mdrivers/staging/rtl8188eu/core/rtw_debug.c | 25+------------------------
Mdrivers/staging/rtl8188eu/core/rtw_efuse.c | 80+++++++++++++++++++++++++++++++++++--------------------------------------------
Mdrivers/staging/rtl8188eu/core/rtw_ieee80211.c | 24+++++++++---------------
Mdrivers/staging/rtl8188eu/core/rtw_ioctl_set.c | 142++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mdrivers/staging/rtl8188eu/core/rtw_led.c | 242++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdrivers/staging/rtl8188eu/core/rtw_mlme.c | 69++++++++++++++++++---------------------------------------------------
Mdrivers/staging/rtl8188eu/core/rtw_mlme_ext.c | 60++++++++++++++++++++++++------------------------------------
Mdrivers/staging/rtl8188eu/core/rtw_pwrctrl.c | 9+++++----
Mdrivers/staging/rtl8188eu/core/rtw_recv.c | 43++++++++++++++++++++++---------------------
Mdrivers/staging/rtl8188eu/core/rtw_security.c | 13++++++-------
Mdrivers/staging/rtl8188eu/core/rtw_wlan_util.c | 73++++++++++++++++++++++++++++++-------------------------------------------
Mdrivers/staging/rtl8188eu/core/rtw_xmit.c | 187+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mdrivers/staging/rtl8188eu/hal/bb_cfg.c | 8++++----
Mdrivers/staging/rtl8188eu/hal/fw.c | 6+++---
Mdrivers/staging/rtl8188eu/hal/hal8188e_rate_adaptive.c | 18++++++++++--------
Mdrivers/staging/rtl8188eu/hal/hal_com.c | 11+++++------
Mdrivers/staging/rtl8188eu/hal/odm.c | 21+++------------------
Ddrivers/staging/rtl8188eu/hal/odm_HWConfig.c | 420-------------------------------------------------------------------------------
Adrivers/staging/rtl8188eu/hal/odm_hwconfig.c | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/rtl8188eu/hal/odm_rtl8188e.c | 67+++++++++++++++++++++++--------------------------------------------
Mdrivers/staging/rtl8188eu/hal/phy.c | 27+++------------------------
Mdrivers/staging/rtl8188eu/hal/pwrseq.c | 5++---
Mdrivers/staging/rtl8188eu/hal/rf_cfg.c | 2+-
Mdrivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c | 101+++++++++++++++++++++----------------------------------------------------------
Mdrivers/staging/rtl8188eu/hal/usb_halinit.c | 26+++++++++++++-------------
Mdrivers/staging/rtl8188eu/include/drv_types.h | 7++++++-
Mdrivers/staging/rtl8188eu/include/hal_com.h | 12++++--------
Ddrivers/staging/rtl8188eu/include/odm_HWConfig.h | 110-------------------------------------------------------------------------------
Adrivers/staging/rtl8188eu/include/odm_hwconfig.h | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/rtl8188eu/include/odm_precomp.h | 4++--
Ddrivers/staging/rtl8188eu/include/odm_reg.h | 106-------------------------------------------------------------------------------
Mdrivers/staging/rtl8188eu/include/osdep_service.h | 2--
Mdrivers/staging/rtl8188eu/include/phy.h | 1-
Mdrivers/staging/rtl8188eu/include/rtw_mlme.h | 3+--
Mdrivers/staging/rtl8188eu/include/rtw_mlme_ext.h | 18+++++++++---------
Ddrivers/staging/rtl8188eu/include/rtw_qos.h | 18------------------
Mdrivers/staging/rtl8188eu/include/wifi.h | 8--------
Mdrivers/staging/rtl8188eu/os_dep/ioctl_linux.c | 25++++++++++++-------------
Mdrivers/staging/rtl8188eu/os_dep/mlme_linux.c | 2+-
Mdrivers/staging/rtl8188eu/os_dep/os_intfs.c | 2+-
Mdrivers/staging/rtl8188eu/os_dep/osdep_service.c | 14--------------
Mdrivers/staging/rtl8188eu/os_dep/usb_ops_linux.c | 4++--
Mdrivers/staging/rtl8188eu/os_dep/xmit_linux.c | 47+++++++++++++++++++++++++++++------------------
Mdrivers/staging/rtl8192e/rtllib_softmac.c | 16++++++++--------
Mdrivers/staging/rtl8192u/ieee80211/dot11d.c | 108++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/staging/rtl8192u/ieee80211/dot11d.h | 77++++++++++++++++++++++-------------------------------------------------------
Mdrivers/staging/rtl8192u/ieee80211/ieee80211.h | 18++++++++----------
Mdrivers/staging/rtl8192u/ieee80211/ieee80211_module.c | 35+++++++++++++++++++----------------
Mdrivers/staging/rtl8192u/ieee80211/ieee80211_rx.c | 12++++++------
Mdrivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c | 14+++++++-------
Mdrivers/staging/rtl8192u/ieee80211/ieee80211_tx.c | 4++--
Mdrivers/staging/rtl8192u/ieee80211/ieee80211_wx.c | 6+++---
Mdrivers/staging/rtl8192u/ieee80211/rtl819x_BA.h | 84+++++++++++++++++++++++++++++++++----------------------------------------------
Mdrivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c | 184++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/staging/rtl8192u/ieee80211/rtl819x_HT.h | 161+++++++++++++++++++++++++------------------------------------------------------
Mdrivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c | 138+++++++++++++++++++++++--------------------------------------------------------
Mdrivers/staging/rtl8192u/ieee80211/rtl819x_TS.h | 6+++---
Mdrivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c | 16++++++++--------
Mdrivers/staging/rtl8192u/r8180_93cx6.h | 19+------------------
Mdrivers/staging/rtl8192u/r8190_rtl8256.c | 33+++++++++++++++++++--------------
Mdrivers/staging/rtl8192u/r8190_rtl8256.h | 11++++++-----
Mdrivers/staging/rtl8192u/r8192U.h | 46+++++++++++++++++++++++-----------------------
Mdrivers/staging/rtl8192u/r8192U_core.c | 105+++++++++++++++++++++++++++++++------------------------------------------------
Mdrivers/staging/rtl8192u/r8192U_hw.h | 204++++++++-----------------------------------------------------------------------
Mdrivers/staging/rtl8192u/r819xU_firmware.c | 4++--
Mdrivers/staging/rtl8192u/r819xU_firmware.h | 11+++++------
Mdrivers/staging/rtl8192u/r819xU_phy.c | 31++++++++++++++++---------------
Mdrivers/staging/rtl8192u/r819xU_phy.h | 6+++++-
Mdrivers/staging/rtl8712/basic_types.h | 10+---------
Mdrivers/staging/rtl8712/drv_types.h | 10+---------
Mdrivers/staging/rtl8712/ethernet.h | 10+---------
Mdrivers/staging/rtl8712/hal_init.c | 10+---------
Mdrivers/staging/rtl8712/ieee80211.c | 10+---------
Mdrivers/staging/rtl8712/ieee80211.h | 13+------------
Mdrivers/staging/rtl8712/mlme_linux.c | 14+-------------
Mdrivers/staging/rtl8712/mlme_osdep.h | 14+-------------
Mdrivers/staging/rtl8712/mp_custom_oid.h | 14+-------------
Mdrivers/staging/rtl8712/os_intfs.c | 10+---------
Mdrivers/staging/rtl8712/osdep_intf.h | 14+-------------
Mdrivers/staging/rtl8712/osdep_service.h | 14+-------------
Mdrivers/staging/rtl8712/recv_linux.c | 14+-------------
Mdrivers/staging/rtl8712/recv_osdep.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_cmd.c | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_cmd.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_debugctrl_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_edcasetting_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_efuse.c | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_event.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_gp_bitdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_gp_regdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_hal.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_interrupt_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_io.c | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_led.c | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_macsetting_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_macsetting_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_powersave_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_powersave_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_ratectrl_regdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_recv.c | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_recv.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_regdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_security_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_spec.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_syscfg_bitdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_syscfg_regdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_timectrl_bitdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_timectrl_regdef.h | 15+--------------
Mdrivers/staging/rtl8712/rtl8712_wmac_bitdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_wmac_regdef.h | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_xmit.c | 14+-------------
Mdrivers/staging/rtl8712/rtl8712_xmit.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_cmd.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_cmd.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_debug.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_eeprom.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_eeprom.h | 15+--------------
Mdrivers/staging/rtl8712/rtl871x_event.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_ht.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_io.c | 20++++----------------
Mdrivers/staging/rtl8712/rtl871x_io.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_ioctl_linux.c | 21++-------------------
Mdrivers/staging/rtl8712/rtl871x_ioctl_rtl.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_ioctl_rtl.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_ioctl_set.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_ioctl_set.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_led.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_mlme.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_mlme.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_mp.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_mp.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_mp_ioctl.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_mp_ioctl.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_pwrctrl.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_pwrctrl.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_recv.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_rf.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_security.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_security.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_sta_mgt.c | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_wlan_sme.h | 14+-------------
Mdrivers/staging/rtl8712/rtl871x_xmit.c | 16++--------------
Mdrivers/staging/rtl8712/rtl871x_xmit.h | 14+-------------
Mdrivers/staging/rtl8712/sta_info.h | 14+-------------
Mdrivers/staging/rtl8712/usb_halinit.c | 14+-------------
Mdrivers/staging/rtl8712/usb_intf.c | 14+-------------
Mdrivers/staging/rtl8712/usb_ops.c | 14+-------------
Mdrivers/staging/rtl8712/usb_ops.h | 14+-------------
Mdrivers/staging/rtl8712/usb_ops_linux.c | 14+-------------
Mdrivers/staging/rtl8712/usb_osintf.h | 14+-------------
Mdrivers/staging/rtl8712/wifi.h | 14+-------------
Mdrivers/staging/rtl8712/wlan_bssdef.h | 14+-------------
Mdrivers/staging/rtl8712/xmit_linux.c | 14+-------------
Mdrivers/staging/rtl8712/xmit_osdep.h | 14+-------------
Mdrivers/staging/rtl8723bs/core/rtw_ap.c | 24++++++++++++------------
Mdrivers/staging/rtl8723bs/core/rtw_debug.c | 2+-
Mdrivers/staging/rtl8723bs/core/rtw_mlme.c | 2+-
Mdrivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 11+++++------
Mdrivers/staging/rtl8723bs/core/rtw_pwrctrl.c | 4++--
Mdrivers/staging/rtl8723bs/core/rtw_security.c | 5++---
Mdrivers/staging/rtl8723bs/hal/hal_com_phycfg.c | 11++++++-----
Mdrivers/staging/rtl8723bs/hal/odm_DIG.c | 4++--
Mdrivers/staging/rtl8723bs/hal/odm_EdcaTurboCheck.c | 2+-
Mdrivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 2+-
Mdrivers/staging/rtl8723bs/include/drv_types.h | 2+-
Mdrivers/staging/rtl8723bs/os_dep/ioctl_linux.c | 30+++++++++++++++---------------
Mdrivers/staging/rtl8723bs/os_dep/sdio_intf.c | 2+-
Mdrivers/staging/rtlwifi/btcoexist/halbtcoutsrc.c | 10+++-------
Mdrivers/staging/rtlwifi/efuse.c | 3++-
Mdrivers/staging/rtlwifi/halmac/rtl_halmac.c | 4++--
Mdrivers/staging/rtlwifi/phydm/phydm_adc_sampling.c | 2+-
Mdrivers/staging/rtlwifi/phydm/phydm_dig.c | 4++--
Mdrivers/staging/rtlwifi/regd.c | 2+-
Mdrivers/staging/rtlwifi/wifi.h | 4----
Mdrivers/staging/rts5208/ms.c | 619++++++++++++++++++++++++++++---------------------------------------------------
Mdrivers/staging/rts5208/rtsx_card.c | 92+++++++++++++++++++++++++++++--------------------------------------------------
Mdrivers/staging/rts5208/rtsx_card.h | 3+--
Mdrivers/staging/rts5208/rtsx_chip.c | 396+++++++++++++++++++++++++++-----------------------------------------------------
Mdrivers/staging/rts5208/rtsx_scsi.c | 108+++++++++++++++++++++++++++-----------------------------------------------------
Mdrivers/staging/rts5208/sd.c | 649+++++++++++++++++++++++++++----------------------------------------------------
Mdrivers/staging/rts5208/spi.c | 141+++++++++++++++++++++++++++-----------------------------------------------------
Mdrivers/staging/rts5208/xd.c | 210+++++++++++++++++++++++++++-----------------------------------------------------
Mdrivers/staging/sm750fb/ddk750_mode.c | 2+-
Mdrivers/staging/sm750fb/ddk750_sii164.c | 8++++----
Mdrivers/staging/sm750fb/sm750.c | 10+++++-----
Mdrivers/staging/speakup/spk_ttyio.c | 4++--
Mdrivers/staging/vboxvideo/TODO | 1-
Mdrivers/staging/vboxvideo/vbox_drv.c | 165++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdrivers/staging/vboxvideo/vbox_drv.h | 86++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdrivers/staging/vboxvideo/vbox_fb.c | 152++++++++++++++++++-------------------------------------------------------------
Mdrivers/staging/vboxvideo/vbox_irq.c | 8++++----
Mdrivers/staging/vboxvideo/vbox_main.c | 185++++++++++++++-----------------------------------------------------------------
Mdrivers/staging/vboxvideo/vbox_mode.c | 922++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mdrivers/staging/vboxvideo/vbox_ttm.c | 78+++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mdrivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c | 235+++++++++++++++++++++----------------------------------------------------------
Mdrivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c | 338++++++++++++++++++-------------------------------------------------------------
Mdrivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c | 883++++++++++++++++++-------------------------------------------------------------
Mdrivers/staging/vc04_services/bcm2835-audio/bcm2835.c | 222+++++++++++++++++++------------------------------------------------------------
Mdrivers/staging/vc04_services/bcm2835-audio/bcm2835.h | 86+++++++++++--------------------------------------------------------------------
Mdrivers/staging/vc04_services/bcm2835-camera/TODO | 6------
Mdrivers/staging/vc04_services/bcm2835-camera/controls.c | 2+-
Mdrivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.c | 10+---------
Ddrivers/staging/vc04_services/interface/vchi/connections/connection.h | 324-------------------------------------------------------------------------------
Ddrivers/staging/vc04_services/interface/vchi/message_drivers/message.h | 196-------------------------------------------------------------------------------
Mdrivers/staging/vc04_services/interface/vchi/vchi.h | 227++++++++++++++++++++++++++-----------------------------------------------------
Mdrivers/staging/vc04_services/interface/vchi/vchi_cfg.h | 2+-
Ddrivers/staging/vc04_services/interface/vchi/vchi_cfg_internal.h | 71-----------------------------------------------------------------------
Mdrivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c | 4+++-
Mdrivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 35+++++++++++++++++++++++++----------
Mdrivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h | 5+++++
Ddrivers/staging/vc04_services/interface/vchiq_arm/vchiq_genversion | 88-------------------------------------------------------------------------------
Mdrivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c | 38++------------------------------------
Mdrivers/staging/vt6655/rxtx.c | 29+++++++++++++++--------------
Mdrivers/staging/wilc1000/Kconfig | 8++++----
Mdrivers/staging/wilc1000/Makefile | 5+----
Mdrivers/staging/wilc1000/coreconfigurator.c | 4++--
Mdrivers/staging/wilc1000/host_interface.c | 277+++++++++++++++----------------------------------------------------------------
Mdrivers/staging/wilc1000/host_interface.h | 19+++++--------------
Mdrivers/staging/wilc1000/linux_mon.c | 3+--
Mdrivers/staging/wilc1000/linux_wlan.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Ddrivers/staging/wilc1000/wilc_debugfs.c | 115-------------------------------------------------------------------------------
Mdrivers/staging/wilc1000/wilc_sdio.c | 56++++++++++++++++++++++++++++++++++----------------------
Mdrivers/staging/wilc1000/wilc_spi.c | 57++++++++++++++++++++++++++++++++++-----------------------
Mdrivers/staging/wilc1000/wilc_wfi_cfgoperations.c | 281+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/staging/wilc1000/wilc_wfi_cfgoperations.h | 4++--
Mdrivers/staging/wilc1000/wilc_wfi_netdevice.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++----
Mdrivers/staging/wilc1000/wilc_wlan.c | 208++++++++++++++++++++++++++++++++++---------------------------------------------
Mdrivers/staging/wilc1000/wilc_wlan.h | 8++++----
Mdrivers/staging/wilc1000/wilc_wlan_cfg.c | 294+++++++++++++++++++++++++++++++------------------------------------------------
Mdrivers/staging/wilc1000/wilc_wlan_cfg.h | 26+++++++++++++++++++++-----
Mdrivers/staging/wilc1000/wilc_wlan_if.h | 4----
Mdrivers/staging/wlan-ng/cfg80211.c | 49++++++++++++++++++++++++-------------------------
Mdrivers/staging/wlan-ng/hfa384x_usb.c | 46+++++++++++++++++++++-------------------------
Mdrivers/staging/wlan-ng/p80211conv.c | 2+-
Mdrivers/staging/wlan-ng/p80211metadef.h | 121+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/staging/wlan-ng/p80211metastruct.h | 3+--
Mdrivers/staging/wlan-ng/p80211netdev.c | 12++++--------
Mdrivers/staging/wlan-ng/p80211req.c | 36++++++++++++++++++------------------
Mdrivers/staging/wlan-ng/prism2fw.c | 37+++++++++++++++++--------------------
Mdrivers/staging/wlan-ng/prism2mib.c | 76++++++++++++++++++++++++++++++++++++++--------------------------------------
Mdrivers/staging/wlan-ng/prism2sta.c | 86+++++++++++++++++++++++++++++++++++++------------------------------------------
Minclude/dt-bindings/iio/qcom,spmi-vadc.h | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
487 files changed, 44538 insertions(+), 16787 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio @@ -199,7 +199,7 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_x_raw What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_y_raw -KernelVersion: 4.18 +KernelVersion: 4.19 Contact: linux-iio@vger.kernel.org Description: Relative position in direction x or y on a pad (may be diff --git a/Documentation/devicetree/bindings/iio/accel/adxl372.txt b/Documentation/devicetree/bindings/iio/accel/adxl372.txt @@ -0,0 +1,33 @@ +Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer + +http://www.analog.com/media/en/technical-documentation/data-sheets/adxl372.pdf + +Required properties: + - compatible : should be "adi,adxl372" + - reg: the I2C address or SPI chip select number for the device + +Required properties for SPI bus usage: + - spi-max-frequency: Max SPI frequency to use + +Optional properties: + - interrupts: interrupt mapping for IRQ as documented in + Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +Example for a I2C device node: + + accelerometer@53 { + compatible = "adi,adxl372"; + reg = <0x53>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + }; + +Example for a SPI device node: + + accelerometer@0 { + compatible = "adi,adxl372"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/mcp3911.txt b/Documentation/devicetree/bindings/iio/adc/mcp3911.txt @@ -0,0 +1,30 @@ +* Microchip MCP3911 Dual channel analog front end (ADC) + +Required properties: + - compatible: Should be "microchip,mcp3911" + - reg: SPI chip select number for the device + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt. + Max frequency for this chip is 20MHz. + +Optional properties: + - clocks: Phandle and clock identifier for sampling clock + - interrupt-parent: Phandle to the parent interrupt controller + - interrupts: IRQ line for the ADC + - microchip,device-addr: Device address when multiple MCP3911 chips are present on the + same SPI bus. Valid values are 0-3. Defaults to 0. + - vref-supply: Phandle to the external reference voltage supply. + +Example: +adc@0 { + compatible = "microchip,mcp3911"; + reg = <0>; + interrupt-parent = <&gpio5>; + interrupts = <15 IRQ_TYPE_EDGE_RISING>; + spi-max-frequency = <20000000>; + microchip,device-addr = <0>; + vref-supply = <&vref_reg>; + clocks = <&xtal>; +}; diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt @@ -1,7 +1,9 @@ -Qualcomm's SPMI PMIC voltage ADC +Qualcomm's SPMI PMIC ADC -SPMI PMIC voltage ADC (VADC) provides interface to clients to read -voltage. The VADC is a 15-bit sigma-delta ADC. +- SPMI PMIC voltage ADC (VADC) provides interface to clients to read + voltage. The VADC is a 15-bit sigma-delta ADC. +- SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read + voltage. The VADC is a 16-bit sigma-delta ADC. VADC node: @@ -9,11 +11,13 @@ VADC node: Usage: required Value type: <string> Definition: Should contain "qcom,spmi-vadc". + Should contain "qcom,spmi-adc5" for PMIC5 ADC driver. + Should contain "qcom,spmi-adc-rev2" for PMIC rev2 ADC driver. - reg: Usage: required Value type: <prop-encoded-array> - Definition: VADC base address and length in the SPMI PMIC register map. + Definition: VADC base address in the SPMI PMIC register map. - #address-cells: Usage: required @@ -45,13 +49,26 @@ Channel node properties: Definition: ADC channel number. See include/dt-bindings/iio/qcom,spmi-vadc.h +- label: + Usage: required for "qcom,spmi-adc5" and "qcom,spmi-adc-rev2" + Value type: <empty> + Definition: ADC input of the platform as seen in the schematics. + For thermistor inputs connected to generic AMUX or GPIO inputs + these can vary across platform for the same pins. Hence select + the platform schematics name for this channel. + - qcom,decimation: Usage: optional Value type: <u32> Definition: This parameter is used to decrease ADC sampling rate. Quicker measurements can be made by reducing decimation ratio. - Valid values are 512, 1024, 2048, 4096. - If property is not found, default value of 512 will be used. + - For compatible property "qcom,spmi-vadc", valid values are + 512, 1024, 2048, 4096. If property is not found, default value + of 512 will be used. + - For compatible property "qcom,spmi-adc5", valid values are 250, 420 + and 840. If property is not found, default value of 840 is used. + - For compatible property "qcom,spmi-adc-rev2", valid values are 256, + 512 and 1024. If property is not present, default value is 1024. - qcom,pre-scaling: Usage: optional @@ -66,21 +83,38 @@ Channel node properties: - qcom,ratiometric: Usage: optional Value type: <empty> - Definition: Channel calibration type. If this property is specified - VADC will use the VDD reference (1.8V) and GND for channel - calibration. If property is not found, channel will be - calibrated with 0.625V and 1.25V reference channels, also - known as absolute calibration. + Definition: Channel calibration type. + - For compatible property "qcom,spmi-vadc", if this property is + specified VADC will use the VDD reference (1.8V) and GND for + channel calibration. If property is not found, channel will be + calibrated with 0.625V and 1.25V reference channels, also + known as absolute calibration. + - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2", + if this property is specified VADC will use the VDD reference + (1.875V) and GND for channel calibration. If property is not found, + channel will be calibrated with 0V and 1.25V reference channels, + also known as absolute calibration. - qcom,hw-settle-time: Usage: optional Value type: <u32> Definition: Time between AMUX getting configured and the ADC starting - conversion. Delay = 100us * (value) for value < 11, and - 2ms * (value - 10) otherwise. - Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800, - 900 us and 1, 2, 4, 6, 8, 10 ms - If property is not found, channel will use 0us. + conversion. The 'hw_settle_time' is an index used from valid values + and programmed in hardware to achieve the hardware settling delay. + - For compatible property "qcom,spmi-vadc" and "qcom,spmi-adc-rev2", + Delay = 100us * (hw_settle_time) for hw_settle_time < 11, + and 2ms * (hw_settle_time - 10) otherwise. + Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800, + 900 us and 1, 2, 4, 6, 8, 10 ms. + If property is not found, channel will use 0us. + - For compatible property "qcom,spmi-adc5", delay = 15us for + value 0, 100us * (value) for values < 11, + and 2ms * (value - 10) otherwise. + Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 800, + 900 us and 1, 2, 4, 6, 8, 10 ms + Certain controller digital versions have valid values of + 15, 100, 200, 300, 400, 500, 600, 700, 1, 2, 4, 8, 16, 32, 64, 128 ms + If property is not found, channel will use 15us. - qcom,avg-samples: Usage: optional @@ -89,13 +123,18 @@ Channel node properties: Averaging provides the option to obtain a single measurement from the ADC that is an average of multiple samples. The value selected is 2^(value). - Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 - If property is not found, 1 sample will be used. + - For compatible property "qcom,spmi-vadc", valid values + are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 + If property is not found, 1 sample will be used. + - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2", + valid values are: 1, 2, 4, 8, 16 + If property is not found, 1 sample will be used. NOTE: -Following channels, also known as reference point channels, are used for -result calibration and their channel configuration nodes should be defined: +For compatible property "qcom,spmi-vadc" following channels, also known as +reference point channels, are used for result calibration and their channel +configuration nodes should be defined: VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV, VADC_GND_REF and VADC_VDD_VADC. @@ -104,7 +143,7 @@ Example: /* VADC node */ pmic_vadc: vadc@3100 { compatible = "qcom,spmi-vadc"; - reg = <0x3100 0x100>; + reg = <0x3100>; interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt b/Documentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt @@ -12,6 +12,8 @@ Required properties: - interrupts: The interrupt number for the ADC device. - #io-channel-cells: Number of cells in an IIO specifier. - hwlocks: Reference to a phandle of a hwlock provider node. +- nvmem-cells: A phandle to the calibration cells provided by eFuse device. +- nvmem-cell-names: Should be "big_scale_calib", "small_scale_calib". Example: @@ -32,5 +34,7 @@ Example: interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; #io-channel-cells = <1>; hwlocks = <&hwlock 4>; + nvmem-cells = <&adc_big_scale>, <&adc_small_scale>; + nvmem-cell-names = "big_scale_calib", "small_scale_calib"; }; }; diff --git a/Documentation/devicetree/bindings/iio/dac/ad5758.txt b/Documentation/devicetree/bindings/iio/dac/ad5758.txt @@ -50,6 +50,9 @@ Required properties: Optional properties: + - reset-gpios : GPIO spec for the RESET pin. If specified, it will be + asserted during driver probe. + - adi,dc-dc-ilim-microamp: The dc-to-dc converter current limit The following values are currently supported [uA]: * 150000 @@ -71,6 +74,8 @@ AD5758 Example: spi-max-frequency = <1000000>; spi-cpha; + reset-gpios = <&gpio 22 0>; + adi,dc-dc-mode = <2>; adi,range-microvolt = <0 10000000>; adi,dc-dc-ilim-microamp = <200000>; diff --git a/Documentation/devicetree/bindings/iio/dac/ltc1660.txt b/Documentation/devicetree/bindings/iio/dac/ltc1660.txt @@ -0,0 +1,21 @@ +* Linear Technology Micropower octal 8-Bit and 10-Bit DACs + +Required properties: + - compatible: Must be one of the following: + "lltc,ltc1660" + "lltc,ltc1665" + - reg: SPI chip select number for the device + - vref-supply: Phandle to the voltage reference supply + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt. + Max frequency for this chip is 5 MHz. + +Example: +dac@0 { + compatible = "lltc,ltc1660"; + reg = <0>; + spi-max-frequency = <5000000>; + vref-supply = <&vref_reg>; +}; diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -20,6 +20,7 @@ Required properties: bindings. Optional properties: + - vddio-supply: regulator phandle for VDDIO supply - mount-matrix: an optional 3x3 mounting rotation matrix - i2c-gate node. These devices also support an auxiliary i2c bus. This is simple enough to be described using the i2c-gate binding. See diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -7,6 +7,7 @@ Required properties: "st,lsm6dsl" "st,lsm6dsm" "st,ism330dlc" + "st,lsm6dso" - reg: i2c address of the sensor / spi cs line Optional properties: diff --git a/Documentation/devicetree/bindings/iio/light/bh1750.txt b/Documentation/devicetree/bindings/iio/light/bh1750.txt @@ -0,0 +1,18 @@ +ROHM BH1750 - ALS, Ambient light sensor + +Required properties: + +- compatible: Must be one of: + "rohm,bh1710" + "rohm,bh1715" + "rohm,bh1721" + "rohm,bh1750" + "rohm,bh1751" +- reg: the I2C address of the sensor + +Example: + +light-sensor@23 { + compatible = "rohm,bh1750"; + reg = <0x23>; +}; diff --git a/Documentation/devicetree/bindings/iio/light/tsl2772.txt b/Documentation/devicetree/bindings/iio/light/tsl2772.txt @@ -0,0 +1,42 @@ +* AMS/TAOS ALS and proximity sensor + +Required properties: + + - compatible: Should be one of + "amstaos,tsl2571" + "amstaos,tsl2671" + "amstaos,tmd2671" + "amstaos,tsl2771" + "amstaos,tmd2771" + "amstaos,tsl2572" + "amstaos,tsl2672" + "amstaos,tmd2672" + "amstaos,tsl2772" + "amstaos,tmd2772" + "avago,apds9930" + - reg: the I2C address of the device + +Optional properties: + + - amstaos,proximity-diodes - proximity diodes to enable. <0>, <1>, or <0 1> + are the only valid values. + - led-max-microamp - current for the proximity LED. Must be 100000, 50000, + 25000, or 13000. + - vdd-supply: phandle to the regulator that provides power to the sensor. + - vddio-supply: phandle to the regulator that provides power to the bus. + - interrupts: the sole interrupt generated by the device + + Refer to interrupt-controller/interrupts.txt for generic interrupt client + node bindings. + +Example: + +tsl2772@39 { + compatible = "amstaos,tsl2772"; + reg = <0x39>; + interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8941_l17>; + vddio-supply = <&pm8941_lvs1>; + amstaos,proximity-diodes = <0>; + led-max-microamp = <100000>; +}; diff --git a/Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt b/Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt @@ -0,0 +1,12 @@ +ST VL53L0X ToF ranging sensor + +Required properties: + - compatible: must be "st,vl53l0x" + - reg: i2c address where to find the device + +Example: + +vl53l0x@29 { + compatible = "st,vl53l0x"; + reg = <0x29>; +}; diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt @@ -21,16 +21,6 @@ adi,adt7490 +/-1C TDM Extended Temp Range I.C adi,adxl345 Three-Axis Digital Accelerometer adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too) ams,iaq-core AMS iAQ-Core VOC Sensor -amstaos,tsl2571 AMS/TAOS ALS and proximity sensor -amstaos,tsl2671 AMS/TAOS ALS and proximity sensor -amstaos,tmd2671 AMS/TAOS ALS and proximity sensor -amstaos,tsl2771 AMS/TAOS ALS and proximity sensor -amstaos,tmd2771 AMS/TAOS ALS and proximity sensor -amstaos,tsl2572 AMS/TAOS ALS and proximity sensor -amstaos,tsl2672 AMS/TAOS ALS and proximity sensor -amstaos,tmd2672 AMS/TAOS ALS and proximity sensor -amstaos,tsl2772 AMS/TAOS ALS and proximity sensor -amstaos,tmd2772 AMS/TAOS ALS and proximity sensor at,24c08 i2c serial eeprom (24cxx) atmel,at97sc3204t i2c trusted platform module (TPM) capella,cm32181 CM32181: Ambient Light Sensor diff --git a/Documentation/filesystems/pohmelfs/design_notes.txt b/Documentation/filesystems/pohmelfs/design_notes.txt @@ -1,72 +0,0 @@ -POHMELFS: Parallel Optimized Host Message Exchange Layered File System. - - Evgeniy Polyakov <zbr@ioremap.net> - -Homepage: http://www.ioremap.net/projects/pohmelfs - -POHMELFS first began as a network filesystem with coherent local data and -metadata caches but is now evolving into a parallel distributed filesystem. - -Main features of this FS include: - * Locally coherent cache for data and metadata with (potentially) byte-range locks. - Since all Linux filesystems lock the whole inode during writing, algorithm - is very simple and does not use byte-ranges, although they are sent in - locking messages. - * Completely async processing of all events except creation of hard and symbolic - links, and rename events. - Object creation and data reading and writing are processed asynchronously. - * Flexible object architecture optimized for network processing. - Ability to create long paths to objects and remove arbitrarily huge - directories with a single network command. - (like removing the whole kernel tree via a single network command). - * Very high performance. - * Fast and scalable multithreaded userspace server. Being in userspace it works - with any underlying filesystem and still is much faster than async in-kernel NFS one. - * Client is able to switch between different servers (if one goes down, client - automatically reconnects to second and so on). - * Transactions support. Full failover for all operations. - Resending transactions to different servers on timeout or error. - * Read request (data read, directory listing, lookup requests) balancing between multiple servers. - * Write requests are replicated to multiple servers and completed only when all of them are acked. - * Ability to add and/or remove servers from the working set at run-time. - * Strong authentication and possible data encryption in network channel. - * Extended attributes support. - -POHMELFS is based on transactions, which are potentially long-standing objects that live -in the client's memory. Each transaction contains all the information needed to process a given -command (or set of commands, which is frequently used during data writing: single transactions -can contain creation and data writing commands). Transactions are committed by all the servers -to which they are sent and, in case of failures, are eventually resent or dropped with an error. -For example, reading will return an error if no servers are available. - -POHMELFS uses a asynchronous approach to data processing. Courtesy of transactions, it is -possible to detach replies from requests and, if the command requires data to be received, the -caller sleeps waiting for it. Thus, it is possible to issue multiple read commands to different -servers and async threads will pick up replies in parallel, find appropriate transactions in the -system and put the data where it belongs (like the page or inode cache). - -The main feature of POHMELFS is writeback data and the metadata cache. -Only a few non-performance critical operations use the write-through cache and -are synchronous: hard and symbolic link creation, and object rename. Creation, -removal of objects and data writing are asynchronous and are sent to -the server during system writeback. Only one writer at a time is allowed for any -given inode, which is guarded by an appropriate locking protocol. -Because of this feature, POHMELFS is extremely fast at metadata intensive -workloads and can fully utilize the bandwidth to the servers when doing bulk -data transfers. - -POHMELFS clients operate with a working set of servers and are capable of balancing read-only -operations (like lookups or directory listings) between them according to IO priorities. -Administrators can add or remove servers from the set at run-time via special commands (described -in Documentation/filesystems/pohmelfs/info.txt file). Writes are replicated to all servers, which -are connected with write permission turned on. IO priority and permissions can be changed in -run-time. - -POHMELFS is capable of full data channel encryption and/or strong crypto hashing. -One can select any kernel supported cipher, encryption mode, hash type and operation mode -(hmac or digest). It is also possible to use both or neither (default). Crypto configuration -is checked during mount time and, if the server does not support it, appropriate capabilities -will be disabled or mount will fail (if 'crypto_fail_unsupported' mount option is specified). -Crypto performance heavily depends on the number of crypto threads, which asynchronously perform -crypto operations and send the resulting data to server or submit it up the stack. This number -can be controlled via a mount option. diff --git a/Documentation/filesystems/pohmelfs/info.txt b/Documentation/filesystems/pohmelfs/info.txt @@ -1,99 +0,0 @@ -POHMELFS usage information. - -Mount options. -All but index, number of crypto threads and maximum IO size can changed via remount. - -idx=%u - Each mountpoint is associated with a special index via this option. - Administrator can add or remove servers from the given index, so all mounts, - which were attached to it, are updated. - Default it is 0. - -trans_scan_timeout=%u - This timeout, expressed in milliseconds, specifies time to scan transaction - trees looking for stale requests, which have to be resent, or if number of - retries exceed specified limit, dropped with error. - Default is 5 seconds. - -drop_scan_timeout=%u - Internal timeout, expressed in milliseconds, which specifies how frequently - inodes marked to be dropped are freed. It also specifies how frequently - the system checks that servers have to be added or removed from current working set. - Default is 1 second. - -wait_on_page_timeout=%u - Number of milliseconds to wait for reply from remote server for data reading command. - If this timeout is exceeded, reading returns an error. - Default is 5 seconds. - -trans_retries=%u - This is the number of times that a transaction will be resent to a server that did - not answer for the last @trans_scan_timeout milliseconds. - When the number of resends exceeds this limit, the transaction is completed with error. - Default is 5 resends. - -crypto_thread_num=%u - Number of crypto processing threads. Threads are used both for RX and TX traffic. - Default is 2, or no threads if crypto operations are not supported. - -trans_max_pages=%u - Maximum number of pages in a single transaction. This parameter also controls - the number of pages, allocated for crypto processing (each crypto thread has - pool of pages, the number of which is equal to 'trans_max_pages'. - Default is 100 pages. - -crypto_fail_unsupported - If specified, mount will fail if the server does not support requested crypto operations. - By default mount will disable non-matching crypto operations. - -mcache_timeout=%u - Maximum number of milliseconds to wait for the mcache objects to be processed. - Mcache includes locks (given lock should be granted by server), attributes (they should be - fully received in the given timeframe). - Default is 5 seconds. - -Usage examples. - -Add server server1.net:1025 into the working set with index $idx -with appropriate hash algorithm and key file and cipher algorithm, mode and key file: -$cfg A add -a server1.net -p 1025 -i $idx -K $hash_key -k $cipher_key - -Mount filesystem with given index $idx to /mnt mountpoint. -Client will connect to all servers specified in the working set via previous command: -mount -t pohmel -o idx=$idx q /mnt - -Change permissions to read-only (-I 1 option, '-I 2' - write-only, 3 - rw): -$cfg A modify -a server1.net -p 1025 -i $idx -I 1 - -Change IO priority to 123 (node with the highest priority gets read requests). -$cfg A modify -a server1.net -p 1025 -i $idx -P 123 - -One can check currect status of all connections in the mountstats file: -# cat /proc/$PID/mountstats -... -device none mounted on /mnt with fstype pohmel -idx addr(:port) socket_type protocol active priority permissions -0 server1.net:1026 1 6 1 250 1 -0 server2.net:1025 1 6 1 123 3 - -Server installation. - -Creating a server, which listens at port 1025 and 0.0.0.0 address. -Working root directory (note, that server chroots there, so you have to have appropriate permissions) -is set to /mnt, server will negotiate hash/cipher with client, in case client requested it, there -are appropriate key files. -Number of working threads is set to 10. - -# ./fserver -a 0.0.0.0 -p 1025 -r /mnt -w 10 -K hash_key -k cipher_key - - -A 6 - listen on ipv6 address. Default: Disabled. - -r root - path to root directory. Default: /tmp. - -a addr - listen address. Default: 0.0.0.0. - -p port - listen port. Default: 1025. - -w workers - number of workers per connected client. Default: 1. - -K file - hash key size. Default: none. - -k file - cipher key size. Default: none. - -h - this help. - -Number of worker threads specifies how many workers will be created for each client. -Bulk single-client transafers usually are better handled with smaller number (like 1-3). diff --git a/Documentation/filesystems/pohmelfs/network_protocol.txt b/Documentation/filesystems/pohmelfs/network_protocol.txt @@ -1,227 +0,0 @@ -POHMELFS network protocol. - -Basic structure used in network communication is following command: - -struct netfs_cmd -{ - __u16 cmd; /* Command number */ - __u16 csize; /* Attached crypto information size */ - __u16 cpad; /* Attached padding size */ - __u16 ext; /* External flags */ - __u32 size; /* Size of the attached data */ - __u32 trans; /* Transaction id */ - __u64 id; /* Object ID to operate on. Used for feedback.*/ - __u64 start; /* Start of the object. */ - __u64 iv; /* IV sequence */ - __u8 data[0]; -}; - -Commands can be embedded into transaction command (which in turn has own command), -so one can extend protocol as needed without breaking backward compatibility as long -as old commands are supported. All string lengths include tail 0 byte. - -All commands are transferred over the network in big-endian. CPU endianness is used at the end peers. - -@cmd - command number, which specifies command to be processed. Following - commands are used currently: - - NETFS_READDIR = 1, /* Read directory for given inode number */ - NETFS_READ_PAGE, /* Read data page from the server */ - NETFS_WRITE_PAGE, /* Write data page to the server */ - NETFS_CREATE, /* Create directory entry */ - NETFS_REMOVE, /* Remove directory entry */ - NETFS_LOOKUP, /* Lookup single object */ - NETFS_LINK, /* Create a link */ - NETFS_TRANS, /* Transaction */ - NETFS_OPEN, /* Open intent */ - NETFS_INODE_INFO, /* Metadata cache coherency synchronization message */ - NETFS_PAGE_CACHE, /* Page cache invalidation message */ - NETFS_READ_PAGES, /* Read multiple contiguous pages in one go */ - NETFS_RENAME, /* Rename object */ - NETFS_CAPABILITIES, /* Capabilities of the client, for example supported crypto */ - NETFS_LOCK, /* Distributed lock message */ - NETFS_XATTR_SET, /* Set extended attribute */ - NETFS_XATTR_GET, /* Get extended attribute */ - -@ext - external flags. Used by different commands to specify some extra arguments - like partial size of the embedded objects or creation flags. - -@size - size of the attached data. For NETFS_READ_PAGE and NETFS_READ_PAGES no data is attached, - but size of the requested data is incorporated here. It does not include size of the command - header (struct netfs_cmd) itself. - -@id - id of the object this command operates on. Each command can use it for own purpose. - -@start - start of the object this command operates on. Each command can use it for own purpose. - -@csize, @cpad - size and padding size of the (attached if needed) crypto information. - -Command specifications. - -@NETFS_READDIR -This command is used to sync content of the remote dir to the client. - -@ext - length of the path to object. -@size - the same. -@id - local inode number of the directory to read. -@start - zero. - - -@NETFS_READ_PAGE -This command is used to read data from remote server. -Data size does not exceed local page cache size. - -@id - inode number. -@start - first byte offset. -@size - number of bytes to read plus length of the path to object. -@ext - object path length. - - -@NETFS_CREATE -Used to create object. -It does not require that all directories on top of the object were -already created, it will create them automatically. Each object has -associated @netfs_path_entry data structure, which contains creation -mode (permissions and type) and length of the name as long as name itself. - -@start - 0 -@size - size of the all data structures needed to create a path -@id - local inode number -@ext - 0 - - -@NETFS_REMOVE -Used to remove object. - -@ext - length of the path to object. -@size - the same. -@id - local inode number. -@start - zero. - - -@NETFS_LOOKUP -Lookup information about object on server. - -@ext - length of the path to object. -@size - the same. -@id - local inode number of the directory to look object in. -@start - local inode number of the object to look at. - - -@NETFS_LINK -Create hard of symlink. -Command is sent as "object_path|target_path". - -@size - size of the above string. -@id - parent local inode number. -@start - 1 for symlink, 0 for hardlink. -@ext - size of the "object_path" above. - - -@NETFS_TRANS -Transaction header. - -@size - incorporates all embedded command sizes including theirs header sizes. -@start - transaction generation number - unique id used to find transaction. -@ext - transaction flags. Unused at the moment. -@id - 0. - - -@NETFS_OPEN -Open intent for given transaction. - -@id - local inode number. -@start - 0. -@size - path length to the object. -@ext - open flags (O_RDWR and so on). - - -@NETFS_INODE_INFO -Metadata update command. -It is sent to servers when attributes of the object are changed and received -when data or metadata were updated. It operates with the following structure: - -struct netfs_inode_info -{ - unsigned int mode; - unsigned int nlink; - unsigned int uid; - unsigned int gid; - unsigned int blocksize; - unsigned int padding; - __u64 ino; - __u64 blocks; - __u64 rdev; - __u64 size; - __u64 version; -}; - -It effectively mirrors stat(2) returned data. - - -@ext - path length to the object. -@size - the same plus size of the netfs_inode_info structure. -@id - local inode number. -@start - 0. - - -@NETFS_PAGE_CACHE -Command is only received by clients. It contains information about -page to be marked as not up-to-date. - -@id - client's inode number. -@start - last byte of the page to be invalidated. If it is not equal to - current inode size, it will be vmtruncated(). -@size - 0 -@ext - 0 - - -@NETFS_READ_PAGES -Used to read multiple contiguous pages in one go. - -@start - first byte of the contiguous region to read. -@size - contains of two fields: lower 8 bits are used to represent page cache shift - used by client, another 3 bytes are used to get number of pages. -@id - local inode number. -@ext - path length to the object. - - -@NETFS_RENAME -Used to rename object. -Attached data is formed into following string: "old_path|new_path". - -@id - local inode number. -@start - parent inode number. -@size - length of the above string. -@ext - length of the old path part. - - -@NETFS_CAPABILITIES -Used to exchange crypto capabilities with server. -If crypto capabilities are not supported by server, then client will disable it -or fail (if 'crypto_fail_unsupported' mount options was specified). - -@id - superblock index. Used to specify crypto information for group of servers. -@size - size of the attached capabilities structure. -@start - 0. -@size - 0. -@scsize - 0. - -@NETFS_LOCK -Used to send lock request/release messages. Although it sends byte range request -and is capable of flushing pages based on that, it is not used, since all Linux -filesystems lock the whole inode. - -@id - lock generation number. -@start - start of the locked range. -@size - size of the locked range. -@ext - lock type: read/write. Not used actually. 15'th bit is used to determine, - if it is lock request (1) or release (0). - -@NETFS_XATTR_SET -@NETFS_XATTR_GET -Used to set/get extended attributes for given inode. -@id - attribute generation number or xattr setting type -@start - size of the attribute (request or attached) -@size - name length, path len and data size for given attribute -@ext - path length for given object diff --git a/MAINTAINERS b/MAINTAINERS @@ -549,6 +549,15 @@ W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/input/misc/adxl34x.c +ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Stefan Popa <stefan.popa@analog.com> +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/accel/adxl372.c +F: drivers/iio/accel/adxl372_spi.c +F: drivers/iio/accel/adxl372_i2c.c +F: Documentation/devicetree/bindings/iio/accel/adxl372.txt + AF9013 MEDIA DRIVER M: Antti Palosaari <crope@iki.fi> L: linux-media@vger.kernel.org @@ -4388,13 +4397,6 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-gpio-mm.c -DIGI NEO AND CLASSIC PCI PRODUCTS -M: Lidza Louina <lidza.louina@gmail.com> -M: Mark Hounschell <markh@compro.net> -L: driverdev-devel@linuxdriverproject.org -S: Maintained -F: drivers/staging/dgnc/ - DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck <linux@roeck-us.net> L: linux-i2c@vger.kernel.org @@ -5675,10 +5677,9 @@ F: Documentation/fault-injection/ F: lib/fault-inject.c FBTFT Framebuffer drivers -M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> +S: Orphan L: dri-devel@lists.freedesktop.org L: linux-fbdev@vger.kernel.org -S: Maintained F: drivers/staging/fbtft/ FC0011 TUNER DRIVER @@ -7737,7 +7738,6 @@ IPX NETWORK LAYER L: netdev@vger.kernel.org S: Obsolete F: include/uapi/linux/ipx.h -F: drivers/staging/ipx/ IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) M: Marc Zyngier <marc.zyngier@arm.com> @@ -8716,6 +8716,13 @@ L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/sym53c8xx_2/ +LTC1660 DAC DRIVER +M: Marcus Folkesson <marcus.folkesson@gmail.com> +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/dac/ltc1660.txt +F: drivers/iio/dac/ltc1660.c + LTC4261 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> L: linux-hwmon@vger.kernel.org @@ -9670,6 +9677,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/microchip/lan743x_* +MICROCHIP / ATMEL MCP3911 ADC DRIVER +M: Marcus Folkesson <marcus.folkesson@gmail.com> +M: Kent Gustavsson <kent@minoris.se> +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/adc/mcp3911.c +F: Documentation/devicetree/bindings/iio/adc/mcp3911.txt + MICROCHIP USB251XB DRIVER M: Richard Leitner <richard.leitner@skidata.com> L: linux-usb@vger.kernel.org @@ -10060,11 +10075,6 @@ NATSEMI ETHERNET DRIVER (DP8381x) S: Orphan F: drivers/net/ethernet/natsemi/natsemi.c -NCP FILESYSTEM -M: Petr Vandrovec <petr@vandrovec.name> -S: Obsolete -F: drivers/staging/ncpfs/ - NCR 5380 SCSI DRIVERS M: Finn Thain <fthain@telegraphics.com.au> M: Michael Schmitz <schmitzmic@gmail.com> @@ -13911,6 +13921,13 @@ L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-stm32* +ST VL53L0X ToF RANGER(I2C) IIO DRIVER +M: Song Qiang <songqiang1304521@gmail.com> +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/proximity/vl53l0x-i2c.c +F: Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt + STABLE BRANCH M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> L: stable@vger.kernel.org @@ -13930,11 +13947,6 @@ L: linux-erofs@lists.ozlabs.org S: Maintained F: drivers/staging/erofs/ -STAGING - FLARION FT1000 DRIVERS -M: Marek Belisko <marek.belisko@gmail.com> -S: Odd Fixes -F: drivers/staging/ft1000/ - STAGING - INDUSTRIAL IO M: Jonathan Cameron <jic23@kernel.org> L: linux-iio@vger.kernel.org diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig @@ -60,6 +60,33 @@ config ADXL345_SPI will be called adxl345_spi and you will also get adxl345_core for the core module. +config ADXL372 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config ADXL372_SPI + tristate "Analog Devices ADXL372 3-Axis Accelerometer SPI Driver" + depends on SPI + select ADXL372 + select REGMAP_SPI + help + Say yes here to add support for the Analog Devices ADXL372 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl372_spi. + +config ADXL372_I2C + tristate "Analog Devices ADXL372 3-Axis Accelerometer I2C Driver" + depends on I2C + select ADXL372 + select REGMAP_I2C + help + Say yes here to add support for the Analog Devices ADXL372 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl372_i2c. + config BMA180 tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile @@ -9,6 +9,9 @@ obj-$(CONFIG_ADIS16209) += adis16209.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o +obj-$(CONFIG_ADXL372) += adxl372.o +obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o +obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c @@ -27,6 +27,9 @@ static int adxl345_i2c_probe(struct i2c_client *client, { struct regmap *regmap; + if (!id) + return -ENODEV; + regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", @@ -35,7 +38,7 @@ static int adxl345_i2c_probe(struct i2c_client *client, } return adxl345_core_probe(&client->dev, regmap, id->driver_data, - id ? id->name : NULL); + id->name); } static int adxl345_i2c_remove(struct i2c_client *client) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c @@ -0,0 +1,975 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL372 3-Axis Digital Accelerometer core driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include "adxl372.h" + +/* ADXL372 registers definition */ +#define ADXL372_DEVID 0x00 +#define ADXL372_DEVID_MST 0x01 +#define ADXL372_PARTID 0x02 +#define ADXL372_STATUS_1 0x04 +#define ADXL372_STATUS_2 0x05 +#define ADXL372_FIFO_ENTRIES_2 0x06 +#define ADXL372_FIFO_ENTRIES_1 0x07 +#define ADXL372_X_DATA_H 0x08 +#define ADXL372_X_DATA_L 0x09 +#define ADXL372_Y_DATA_H 0x0A +#define ADXL372_Y_DATA_L 0x0B +#define ADXL372_Z_DATA_H 0x0C +#define ADXL372_Z_DATA_L 0x0D +#define ADXL372_X_MAXPEAK_H 0x15 +#define ADXL372_X_MAXPEAK_L 0x16 +#define ADXL372_Y_MAXPEAK_H 0x17 +#define ADXL372_Y_MAXPEAK_L 0x18 +#define ADXL372_Z_MAXPEAK_H 0x19 +#define ADXL372_Z_MAXPEAK_L 0x1A +#define ADXL372_OFFSET_X 0x20 +#define ADXL372_OFFSET_Y 0x21 +#define ADXL372_OFFSET_Z 0x22 +#define ADXL372_X_THRESH_ACT_H 0x23 +#define ADXL372_X_THRESH_ACT_L 0x24 +#define ADXL372_Y_THRESH_ACT_H 0x25 +#define ADXL372_Y_THRESH_ACT_L 0x26 +#define ADXL372_Z_THRESH_ACT_H 0x27 +#define ADXL372_Z_THRESH_ACT_L 0x28 +#define ADXL372_TIME_ACT 0x29 +#define ADXL372_X_THRESH_INACT_H 0x2A +#define ADXL372_X_THRESH_INACT_L 0x2B +#define ADXL372_Y_THRESH_INACT_H 0x2C +#define ADXL372_Y_THRESH_INACT_L 0x2D +#define ADXL372_Z_THRESH_INACT_H 0x2E +#define ADXL372_Z_THRESH_INACT_L 0x2F +#define ADXL372_TIME_INACT_H 0x30 +#define ADXL372_TIME_INACT_L 0x31 +#define ADXL372_X_THRESH_ACT2_H 0x32 +#define ADXL372_X_THRESH_ACT2_L 0x33 +#define ADXL372_Y_THRESH_ACT2_H 0x34 +#define ADXL372_Y_THRESH_ACT2_L 0x35 +#define ADXL372_Z_THRESH_ACT2_H 0x36 +#define ADXL372_Z_THRESH_ACT2_L 0x37 +#define ADXL372_HPF 0x38 +#define ADXL372_FIFO_SAMPLES 0x39 +#define ADXL372_FIFO_CTL 0x3A +#define ADXL372_INT1_MAP 0x3B +#define ADXL372_INT2_MAP 0x3C +#define ADXL372_TIMING 0x3D +#define ADXL372_MEASURE 0x3E +#define ADXL372_POWER_CTL 0x3F +#define ADXL372_SELF_TEST 0x40 +#define ADXL372_RESET 0x41 +#define ADXL372_FIFO_DATA 0x42 + +#define ADXL372_DEVID_VAL 0xAD +#define ADXL372_PARTID_VAL 0xFA +#define ADXL372_RESET_CODE 0x52 + +/* ADXL372_POWER_CTL */ +#define ADXL372_POWER_CTL_MODE_MSK GENMASK_ULL(1, 0) +#define ADXL372_POWER_CTL_MODE(x) (((x) & 0x3) << 0) + +/* ADXL372_MEASURE */ +#define ADXL372_MEASURE_LINKLOOP_MSK GENMASK_ULL(5, 4) +#define ADXL372_MEASURE_LINKLOOP_MODE(x) (((x) & 0x3) << 4) +#define ADXL372_MEASURE_BANDWIDTH_MSK GENMASK_ULL(2, 0) +#define ADXL372_MEASURE_BANDWIDTH_MODE(x) (((x) & 0x7) << 0) + +/* ADXL372_TIMING */ +#define ADXL372_TIMING_ODR_MSK GENMASK_ULL(7, 5) +#define ADXL372_TIMING_ODR_MODE(x) (((x) & 0x7) << 5) + +/* ADXL372_FIFO_CTL */ +#define ADXL372_FIFO_CTL_FORMAT_MSK GENMASK(5, 3) +#define ADXL372_FIFO_CTL_FORMAT_MODE(x) (((x) & 0x7) << 3) +#define ADXL372_FIFO_CTL_MODE_MSK GENMASK(2, 1) +#define ADXL372_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 1) +#define ADXL372_FIFO_CTL_SAMPLES_MSK BIT(1) +#define ADXL372_FIFO_CTL_SAMPLES_MODE(x) (((x) > 0xFF) ? 1 : 0) + +/* ADXL372_STATUS_1 */ +#define ADXL372_STATUS_1_DATA_RDY(x) (((x) >> 0) & 0x1) +#define ADXL372_STATUS_1_FIFO_RDY(x) (((x) >> 1) & 0x1) +#define ADXL372_STATUS_1_FIFO_FULL(x) (((x) >> 2) & 0x1) +#define ADXL372_STATUS_1_FIFO_OVR(x) (((x) >> 3) & 0x1) +#define ADXL372_STATUS_1_USR_NVM_BUSY(x) (((x) >> 5) & 0x1) +#define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1) +#define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1) + +/* ADXL372_INT1_MAP */ +#define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0) +#define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0) +#define ADXL372_INT1_MAP_FIFO_RDY_MSK BIT(1) +#define ADXL372_INT1_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1) +#define ADXL372_INT1_MAP_FIFO_FULL_MSK BIT(2) +#define ADXL372_INT1_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2) +#define ADXL372_INT1_MAP_FIFO_OVR_MSK BIT(3) +#define ADXL372_INT1_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3) +#define ADXL372_INT1_MAP_INACT_MSK BIT(4) +#define ADXL372_INT1_MAP_INACT_MODE(x) (((x) & 0x1) << 4) +#define ADXL372_INT1_MAP_ACT_MSK BIT(5) +#define ADXL372_INT1_MAP_ACT_MODE(x) (((x) & 0x1) << 5) +#define ADXL372_INT1_MAP_AWAKE_MSK BIT(6) +#define ADXL372_INT1_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6) +#define ADXL372_INT1_MAP_LOW_MSK BIT(7) +#define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) + +/* The ADXL372 includes a deep, 512 sample FIFO buffer */ +#define ADXL372_FIFO_SIZE 512 + +/* + * At +/- 200g with 12-bit resolution, scale is computed as: + * (200 + 200) * 9.81 / (2^12 - 1) = 0.958241 + */ +#define ADXL372_USCALE 958241 + +enum adxl372_op_mode { + ADXL372_STANDBY, + ADXL372_WAKE_UP, + ADXL372_INSTANT_ON, + ADXL372_FULL_BW_MEASUREMENT, +}; + +enum adxl372_act_proc_mode { + ADXL372_DEFAULT, + ADXL372_LINKED, + ADXL372_LOOPED, +}; + +enum adxl372_th_activity { + ADXL372_ACTIVITY, + ADXL372_ACTIVITY2, + ADXL372_INACTIVITY, +}; + +enum adxl372_odr { + ADXL372_ODR_400HZ, + ADXL372_ODR_800HZ, + ADXL372_ODR_1600HZ, + ADXL372_ODR_3200HZ, + ADXL372_ODR_6400HZ, +}; + +enum adxl372_bandwidth { + ADXL372_BW_200HZ, + ADXL372_BW_400HZ, + ADXL372_BW_800HZ, + ADXL372_BW_1600HZ, + ADXL372_BW_3200HZ, +}; + +static const unsigned int adxl372_th_reg_high_addr[3] = { + [ADXL372_ACTIVITY] = ADXL372_X_THRESH_ACT_H, + [ADXL372_ACTIVITY2] = ADXL372_X_THRESH_ACT2_H, + [ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H, +}; + +enum adxl372_fifo_format { + ADXL372_XYZ_FIFO, + ADXL372_X_FIFO, + ADXL372_Y_FIFO, + ADXL372_XY_FIFO, + ADXL372_Z_FIFO, + ADXL372_XZ_FIFO, + ADXL372_YZ_FIFO, + ADXL372_XYZ_PEAK_FIFO, +}; + +enum adxl372_fifo_mode { + ADXL372_FIFO_BYPASSED, + ADXL372_FIFO_STREAMED, + ADXL372_FIFO_TRIGGERED, + ADXL372_FIFO_OLD_SAVED +}; + +static const int adxl372_samp_freq_tbl[5] = { + 400, 800, 1600, 3200, 6400, +}; + +static const int adxl372_bw_freq_tbl[5] = { + 200, 400, 800, 1600, 3200, +}; + +struct adxl372_axis_lookup { + unsigned int bits; + enum adxl372_fifo_format fifo_format; +}; + +static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { + { BIT(0), ADXL372_X_FIFO }, + { BIT(1), ADXL372_Y_FIFO }, + { BIT(2), ADXL372_Z_FIFO }, + { BIT(0) | BIT(1), ADXL372_XY_FIFO }, + { BIT(0) | BIT(2), ADXL372_XZ_FIFO }, + { BIT(1) | BIT(2), ADXL372_YZ_FIFO }, + { BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO }, +}; + +#define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + }, \ +} + +static const struct iio_chan_spec adxl372_channels[] = { + ADXL372_ACCEL_CHANNEL(0, ADXL372_X_DATA_H, X), + ADXL372_ACCEL_CHANNEL(1, ADXL372_Y_DATA_H, Y), + ADXL372_ACCEL_CHANNEL(2, ADXL372_Z_DATA_H, Z), +}; + +struct adxl372_state { + int irq; + struct device *dev; + struct regmap *regmap; + struct iio_trigger *dready_trig; + enum adxl372_fifo_mode fifo_mode; + enum adxl372_fifo_format fifo_format; + enum adxl372_op_mode op_mode; + enum adxl372_act_proc_mode act_proc_mode; + enum adxl372_odr odr; + enum adxl372_bandwidth bw; + u32 act_time_ms; + u32 inact_time_ms; + u8 fifo_set_size; + u8 int1_bitmask; + u8 int2_bitmask; + u16 watermark; + __be16 fifo_buf[ADXL372_FIFO_SIZE]; +}; + +static const unsigned long adxl372_channel_masks[] = { + BIT(0), BIT(1), BIT(2), + BIT(0) | BIT(1), + BIT(0) | BIT(2), + BIT(1) | BIT(2), + BIT(0) | BIT(1) | BIT(2), + 0 +}; + +static int adxl372_read_axis(struct adxl372_state *st, u8 addr) +{ + __be16 regval; + int ret; + + ret = regmap_bulk_read(st->regmap, addr, &regval, sizeof(regval)); + if (ret < 0) + return ret; + + return be16_to_cpu(regval); +} + +static int adxl372_set_op_mode(struct adxl372_state *st, + enum adxl372_op_mode op_mode) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_MODE_MSK, + ADXL372_POWER_CTL_MODE(op_mode)); + if (ret < 0) + return ret; + + st->op_mode = op_mode; + + return ret; +} + +static int adxl372_set_odr(struct adxl372_state *st, + enum adxl372_odr odr) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL372_TIMING, + ADXL372_TIMING_ODR_MSK, + ADXL372_TIMING_ODR_MODE(odr)); + if (ret < 0) + return ret; + + st->odr = odr; + + return ret; +} + +static int adxl372_find_closest_match(const int *array, + unsigned int size, int val) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= array[i]) + return i; + } + + return size - 1; +} + +static int adxl372_set_bandwidth(struct adxl372_state *st, + enum adxl372_bandwidth bw) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL372_MEASURE, + ADXL372_MEASURE_BANDWIDTH_MSK, + ADXL372_MEASURE_BANDWIDTH_MODE(bw)); + if (ret < 0) + return ret; + + st->bw = bw; + + return ret; +} + +static int adxl372_set_act_proc_mode(struct adxl372_state *st, + enum adxl372_act_proc_mode mode) +{ + int ret; + + ret = regmap_update_bits(st->regmap, + ADXL372_MEASURE, + ADXL372_MEASURE_LINKLOOP_MSK, + ADXL372_MEASURE_LINKLOOP_MODE(mode)); + if (ret < 0) + return ret; + + st->act_proc_mode = mode; + + return ret; +} + +static int adxl372_set_activity_threshold(struct adxl372_state *st, + enum adxl372_th_activity act, + bool ref_en, bool enable, + unsigned int threshold) +{ + unsigned char buf[6]; + unsigned char th_reg_high_val, th_reg_low_val, th_reg_high_addr; + + /* scale factor is 100 mg/code */ + th_reg_high_val = (threshold / 100) >> 3; + th_reg_low_val = ((threshold / 100) << 5) | (ref_en << 1) | enable; + th_reg_high_addr = adxl372_th_reg_high_addr[act]; + + buf[0] = th_reg_high_val; + buf[1] = th_reg_low_val; + buf[2] = th_reg_high_val; + buf[3] = th_reg_low_val; + buf[4] = th_reg_high_val; + buf[5] = th_reg_low_val; + + return regmap_bulk_write(st->regmap, th_reg_high_addr, + buf, ARRAY_SIZE(buf)); +} + +static int adxl372_set_activity_time_ms(struct adxl372_state *st, + unsigned int act_time_ms) +{ + unsigned int reg_val, scale_factor; + int ret; + + /* + * 3.3 ms per code is the scale factor of the TIME_ACT register for + * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below. + */ + if (st->odr == ADXL372_ODR_6400HZ) + scale_factor = 3300; + else + scale_factor = 6600; + + reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor); + + /* TIME_ACT register is 8 bits wide */ + if (reg_val > 0xFF) + reg_val = 0xFF; + + ret = regmap_write(st->regmap, ADXL372_TIME_ACT, reg_val); + if (ret < 0) + return ret; + + st->act_time_ms = act_time_ms; + + return ret; +} + +static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, + unsigned int inact_time_ms) +{ + unsigned int reg_val_h, reg_val_l, res, scale_factor; + int ret; + + /* + * 13 ms per code is the scale factor of the TIME_INACT register for + * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below. + */ + if (st->odr == ADXL372_ODR_6400HZ) + scale_factor = 13; + else + scale_factor = 26; + + res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor); + reg_val_h = (res >> 8) & 0xFF; + reg_val_l = res & 0xFF; + + ret = regmap_write(st->regmap, ADXL372_TIME_INACT_H, reg_val_h); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, ADXL372_TIME_INACT_L, reg_val_l); + if (ret < 0) + return ret; + + st->inact_time_ms = inact_time_ms; + + return ret; +} + +static int adxl372_set_interrupts(struct adxl372_state *st, + unsigned char int1_bitmask, + unsigned char int2_bitmask) +{ + int ret; + + ret = regmap_write(st->regmap, ADXL372_INT1_MAP, int1_bitmask); + if (ret < 0) + return ret; + + return regmap_write(st->regmap, ADXL372_INT2_MAP, int2_bitmask); +} + +static int adxl372_configure_fifo(struct adxl372_state *st) +{ + unsigned int fifo_samples, fifo_ctl; + int ret; + + /* FIFO must be configured while in standby mode */ + ret = adxl372_set_op_mode(st, ADXL372_STANDBY); + if (ret < 0) + return ret; + + fifo_samples = st->watermark & 0xFF; + fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) | + ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) | + ADXL372_FIFO_CTL_SAMPLES_MODE(st->watermark); + + ret = regmap_write(st->regmap, ADXL372_FIFO_SAMPLES, fifo_samples); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, ADXL372_FIFO_CTL, fifo_ctl); + if (ret < 0) + return ret; + + return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); +} + +static int adxl372_get_status(struct adxl372_state *st, + u8 *status1, u8 *status2, + u16 *fifo_entries) +{ + __be32 buf; + u32 val; + int ret; + + /* STATUS1, STATUS2, FIFO_ENTRIES2 and FIFO_ENTRIES are adjacent regs */ + ret = regmap_bulk_read(st->regmap, ADXL372_STATUS_1, + &buf, sizeof(buf)); + if (ret < 0) + return ret; + + val = be32_to_cpu(buf); + + *status1 = (val >> 24) & 0x0F; + *status2 = (val >> 16) & 0x0F; + /* + * FIFO_ENTRIES contains the least significant byte, and FIFO_ENTRIES2 + * contains the two most significant bits + */ + *fifo_entries = val & 0x3FF; + + return ret; +} + +static irqreturn_t adxl372_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adxl372_state *st = iio_priv(indio_dev); + u8 status1, status2; + u16 fifo_entries; + int i, ret; + + ret = adxl372_get_status(st, &status1, &status2, &fifo_entries); + if (ret < 0) + goto err; + + if (st->fifo_mode != ADXL372_FIFO_BYPASSED && + ADXL372_STATUS_1_FIFO_FULL(status1)) { + /* + * When reading data from multiple axes from the FIFO, + * to ensure that data is not overwritten and stored out + * of order at least one sample set must be left in the + * FIFO after every read. + */ + fifo_entries -= st->fifo_set_size; + + /* Read data from the FIFO */ + ret = regmap_noinc_read(st->regmap, ADXL372_FIFO_DATA, + st->fifo_buf, + fifo_entries * sizeof(u16)); + if (ret < 0) + goto err; + + /* Each sample is 2 bytes */ + for (i = 0; i < fifo_entries * sizeof(u16); + i += st->fifo_set_size * sizeof(u16)) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + } +err: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int adxl372_setup(struct adxl372_state *st) +{ + unsigned int regval; + int ret; + + ret = regmap_read(st->regmap, ADXL372_DEVID, &regval); + if (ret < 0) + return ret; + + if (regval != ADXL372_DEVID_VAL) { + dev_err(st->dev, "Invalid chip id %x\n", regval); + return -ENODEV; + } + + ret = adxl372_set_op_mode(st, ADXL372_STANDBY); + if (ret < 0) + return ret; + + /* Set threshold for activity detection to 1g */ + ret = adxl372_set_activity_threshold(st, ADXL372_ACTIVITY, + true, true, 1000); + if (ret < 0) + return ret; + + /* Set threshold for inactivity detection to 100mg */ + ret = adxl372_set_activity_threshold(st, ADXL372_INACTIVITY, + true, true, 100); + if (ret < 0) + return ret; + + /* Set activity processing in Looped mode */ + ret = adxl372_set_act_proc_mode(st, ADXL372_LOOPED); + if (ret < 0) + return ret; + + ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ); + if (ret < 0) + return ret; + + ret = adxl372_set_bandwidth(st, ADXL372_BW_3200HZ); + if (ret < 0) + return ret; + + /* Set activity timer to 1ms */ + ret = adxl372_set_activity_time_ms(st, 1); + if (ret < 0) + return ret; + + /* Set inactivity timer to 10s */ + ret = adxl372_set_inactivity_time_ms(st, 10000); + if (ret < 0) + return ret; + + /* Set the mode of operation to full bandwidth measurement mode */ + return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); +} + +static int adxl372_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static int adxl372_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = adxl372_read_axis(st, chan->address); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = ADXL372_USCALE; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adxl372_samp_freq_tbl[st->odr]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = adxl372_bw_freq_tbl[st->bw]; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int adxl372_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int odr_index, bw_index, ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl, + ARRAY_SIZE(adxl372_samp_freq_tbl), + val); + ret = adxl372_set_odr(st, odr_index); + if (ret < 0) + return ret; + /* + * The timer period depends on the ODR selected. + * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms + */ + ret = adxl372_set_activity_time_ms(st, st->act_time_ms); + if (ret < 0) + return ret; + /* + * The timer period depends on the ODR selected. + * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms + */ + ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms); + if (ret < 0) + return ret; + /* + * The maximum bandwidth is constrained to at most half of + * the ODR to ensure that the Nyquist criteria is not violated + */ + if (st->bw > odr_index) + ret = adxl372_set_bandwidth(st, odr_index); + + return ret; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl, + ARRAY_SIZE(adxl372_bw_freq_tbl), + val); + return adxl372_set_bandwidth(st, bw_index); + default: + return -EINVAL; + } +} + +static ssize_t adxl372_show_filter_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl372_state *st = iio_priv(indio_dev); + int i; + size_t len = 0; + + for (i = 0; i <= st->odr; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d ", adxl372_bw_freq_tbl[i]); + + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t adxl372_get_fifo_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl372_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->fifo_mode); +} + +static ssize_t adxl372_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl372_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->watermark); +} + +static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); +static IIO_CONST_ATTR(hwfifo_watermark_max, + __stringify(ADXL372_FIFO_SIZE)); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + adxl372_get_fifo_watermark, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + adxl372_get_fifo_enabled, NULL, 0); + +static const struct attribute *adxl372_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int adxl372_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + if (val > ADXL372_FIFO_SIZE) + val = ADXL372_FIFO_SIZE; + + st->watermark = val; + + return 0; +} + +static int adxl372_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + unsigned int mask; + int i, ret; + + ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); + if (ret < 0) + return ret; + + mask = *indio_dev->active_scan_mask; + + for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) { + if (mask == adxl372_axis_lookup_table[i].bits) + break; + } + + if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) + return -EINVAL; + + st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; + st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength); + /* + * The 512 FIFO samples can be allotted in several ways, such as: + * 170 sample sets of concurrent 3-axis data + * 256 sample sets of concurrent 2-axis data (user selectable) + * 512 sample sets of single-axis data + */ + if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE) + st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size); + + st->fifo_mode = ADXL372_FIFO_STREAMED; + + ret = adxl372_configure_fifo(st); + if (ret < 0) { + st->fifo_mode = ADXL372_FIFO_BYPASSED; + adxl372_set_interrupts(st, 0, 0); + return ret; + } + + return iio_triggered_buffer_postenable(indio_dev); +} + +static int adxl372_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret < 0) + return ret; + + adxl372_set_interrupts(st, 0, 0); + st->fifo_mode = ADXL372_FIFO_BYPASSED; + adxl372_configure_fifo(st); + + return 0; +} + +static const struct iio_buffer_setup_ops adxl372_buffer_ops = { + .postenable = adxl372_buffer_postenable, + .predisable = adxl372_buffer_predisable, +}; + +static int adxl372_dready_trig_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct adxl372_state *st = iio_priv(indio_dev); + unsigned long int mask = 0; + + if (state) + mask = ADXL372_INT1_MAP_FIFO_FULL_MSK; + + return adxl372_set_interrupts(st, mask, 0); +} + +static int adxl372_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + if (st->dready_trig != trig) + return -EINVAL; + + return 0; +} + +static const struct iio_trigger_ops adxl372_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, + .set_trigger_state = adxl372_dready_trig_set_state, +}; + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); +static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, + 0444, adxl372_show_filter_freq_avail, NULL, 0); + +static struct attribute *adxl372_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group adxl372_attrs_group = { + .attrs = adxl372_attributes, +}; + +static const struct iio_info adxl372_info = { + .validate_trigger = &adxl372_validate_trigger, + .attrs = &adxl372_attrs_group, + .read_raw = adxl372_read_raw, + .write_raw = adxl372_write_raw, + .debugfs_reg_access = &adxl372_reg_access, + .hwfifo_set_watermark = adxl372_set_watermark, +}; + +bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) +{ + return (reg == ADXL372_FIFO_DATA); +} +EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg); + +int adxl372_probe(struct device *dev, struct regmap *regmap, + int irq, const char *name) +{ + struct iio_dev *indio_dev; + struct adxl372_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + st->dev = dev; + st->regmap = regmap; + st->irq = irq; + + indio_dev->channels = adxl372_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); + indio_dev->available_scan_masks = adxl372_channel_masks; + indio_dev->dev.parent = dev; + indio_dev->name = name; + indio_dev->info = &adxl372_info; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + + ret = adxl372_setup(st); + if (ret < 0) { + dev_err(dev, "ADXL372 setup failed\n"); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, + indio_dev, NULL, + adxl372_trigger_handler, + &adxl372_buffer_ops); + if (ret < 0) + return ret; + + iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); + + if (st->irq) { + st->dready_trig = devm_iio_trigger_alloc(dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (st->dready_trig == NULL) + return -ENOMEM; + + st->dready_trig->ops = &adxl372_trigger_ops; + st->dready_trig->dev.parent = dev; + iio_trigger_set_drvdata(st->dready_trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->dready_trig); + if (ret < 0) + return ret; + + indio_dev->trig = iio_trigger_get(st->dready_trig); + + ret = devm_request_threaded_irq(dev, st->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + indio_dev->name, st->dready_trig); + if (ret < 0) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(adxl372_probe); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ADXL372 3-Axis Digital Accelerometer + * + * Copyright 2018 Analog Devices Inc. + */ + +#ifndef _ADXL372_H_ +#define _ADXL372_H_ + +#define ADXL372_REVID 0x03 + +int adxl372_probe(struct device *dev, struct regmap *regmap, + int irq, const char *name); +bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg); + +#endif /* _ADXL372_H_ */ diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL372 3-Axis Digital Accelerometer I2C driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl372.h" + +static const struct regmap_config adxl372_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .readable_noinc_reg = adxl372_readable_noinc_reg, +}; + +static int adxl372_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + unsigned int regval; + int ret; + + regmap = devm_regmap_init_i2c(client, &adxl372_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_read(regmap, ADXL372_REVID, &regval); + if (ret < 0) + return ret; + + /* Starting with the 3rd revision an I2C chip bug was fixed */ + if (regval < 3) + dev_warn(&client->dev, + "I2C might not work properly with other devices on the bus"); + + return adxl372_probe(&client->dev, regmap, client->irq, id->name); +} + +static const struct i2c_device_id adxl372_i2c_id[] = { + { "adxl372", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); + +static struct i2c_driver adxl372_i2c_driver = { + .driver = { + .name = "adxl372_i2c", + }, + .probe = adxl372_i2c_probe, + .id_table = adxl372_i2c_id, +}; + +module_i2c_driver(adxl372_i2c_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL372 3-Axis Digital Accelerometer SPI driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "adxl372.h" + +static const struct regmap_config adxl372_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), + .readable_noinc_reg = adxl372_readable_noinc_reg, +}; + +static int adxl372_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adxl372_probe(&spi->dev, regmap, spi->irq, id->name); +} + +static const struct spi_device_id adxl372_spi_id[] = { + { "adxl372", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adxl372_spi_id); + +static struct spi_driver adxl372_spi_driver = { + .driver = { + .name = "adxl372_spi", + }, + .probe = adxl372_spi_probe, + .id_table = adxl372_spi_id, +}; + +module_spi_driver(adxl372_spi_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig @@ -501,6 +501,16 @@ config MCP3422 This driver can also be built as a module. If so, the module will be called mcp3422. +config MCP3911 + tristate "Microchip Technology MCP3911 driver" + depends on SPI + help + Say yes here to build support for Microchip Technology's MCP3911 + analog to digital converter. + + This driver can also be built as a module. If so, the module will be + called mcp3911. + config MEDIATEK_MT6577_AUXADC tristate "MediaTek AUXADC driver" depends on ARCH_MEDIATEK || COMPILE_TEST @@ -596,6 +606,26 @@ config QCOM_SPMI_VADC To compile this driver as a module, choose M here: the module will be called qcom-spmi-vadc. +config QCOM_SPMI_ADC5 + tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC" + depends on SPMI + select REGMAP_SPMI + select QCOM_VADC_COMMON + help + This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc. + + The driver supports multiple channels read. The ADC is a 16-bit + sigma-delta ADC. The hardware supports calibrated results for + conversion requests and clients include reading voltage phone + power, on board system thermistors connected to the PMIC ADC, + PMIC die temperature, charger temperature, battery current, USB voltage + input, voltage signals connected to supported PMIC GPIO inputs. The + hardware supports internal pull-up for thermistors and can choose between + a 100k, 30k and 400k pull up using the ADC channels. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-adc5. + config RCAR_GYRO_ADC tristate "Renesas R-Car GyroADC driver" depends on ARCH_RCAR_GEN2 || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile @@ -47,12 +47,14 @@ obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o +obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c @@ -385,6 +385,6 @@ static struct spi_driver ad7298_driver = { }; module_spi_driver(ad7298_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c @@ -328,6 +328,6 @@ static struct spi_driver ad7476_driver = { }; module_spi_driver(ad7476_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c @@ -822,6 +822,6 @@ static struct spi_driver ad7793_driver = { }; module_spi_driver(ad7793_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c @@ -362,6 +362,6 @@ static struct spi_driver ad7887_driver = { }; module_spi_driver(ad7887_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c @@ -363,7 +363,7 @@ static struct spi_driver ad7923_driver = { }; module_spi_driver(ad7923_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>"); MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c @@ -892,6 +892,6 @@ static struct i2c_driver ad799x_driver = { }; module_i2c_driver(ad799x_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD799x ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c @@ -248,12 +248,14 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *idev = pf->indio_dev; struct at91_adc_state *st = iio_priv(idev); + 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; - st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, i)); + chan = idev->channels + i; + st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel)); j++; } @@ -279,6 +281,8 @@ static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev) iio_trigger_poll(idev->trig); } else { st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb)); + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_ADC_LCDR); st->done = true; wake_up_interruptible(&st->wq_data_avail); } diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for an envelope detector using a DAC and a comparator * * Copyright (C) 2016 Axentia Technologies AB * * Author: Peter Rosin <peda@axentia.se> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /* diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c @@ -209,12 +209,14 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, ret = of_property_read_u32(child, "reg", &reg); if (ret) { dev_err(dev, "Failed to get reg property\n"); + of_node_put(child); return ret; } if (reg >= MX25_NUM_CFGS) { dev_err(dev, "reg value is greater than the number of available configuration registers\n"); + of_node_put(child); return -EINVAL; } @@ -228,6 +230,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, if (IS_ERR(priv->vref[refp])) { dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.", mx25_gcq_refp_names[refp]); + of_node_put(child); return PTR_ERR(priv->vref[refp]); } priv->channel_vref_mv[reg] = @@ -240,6 +243,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, break; default: dev_err(dev, "Invalid positive reference %d\n", refp); + of_node_put(child); return -EINVAL; } @@ -254,10 +258,12 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) { dev_err(dev, "Invalid fsl,adc-refp property value\n"); + of_node_put(child); return -EINVAL; } if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) { dev_err(dev, "Invalid fsl,adc-refn property value\n"); + of_node_put(child); return -EINVAL; } diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c @@ -289,7 +289,7 @@ static int max9611_read_csa_voltage(struct max9611_dev *max9611, return ret; if (*adc_raw > 0) { - *csa_gain = gain_selectors[i]; + *csa_gain = (enum max9611_csa_gain)gain_selectors[i]; return 0; } } diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Microchip MCP3911, Two-channel Analog Front End + * + * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com> + * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se> + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#define MCP3911_REG_CHANNEL0 0x00 +#define MCP3911_REG_CHANNEL1 0x03 +#define MCP3911_REG_MOD 0x06 +#define MCP3911_REG_PHASE 0x07 +#define MCP3911_REG_GAIN 0x09 + +#define MCP3911_REG_STATUSCOM 0x0a +#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) +#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) +#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2) +#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1) + +#define MCP3911_REG_CONFIG 0x0c +#define MCP3911_CONFIG_CLKEXT BIT(1) +#define MCP3911_CONFIG_VREFEXT BIT(2) + +#define MCP3911_REG_OFFCAL_CH0 0x0e +#define MCP3911_REG_GAINCAL_CH0 0x11 +#define MCP3911_REG_OFFCAL_CH1 0x14 +#define MCP3911_REG_GAINCAL_CH1 0x17 +#define MCP3911_REG_VREFCAL 0x1a + +#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3) +#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6) + +/* Internal voltage reference in uV */ +#define MCP3911_INT_VREF_UV 1200000 + +#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff) +#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff) + +#define MCP3911_NUM_CHANNELS 2 + +struct mcp3911 { + struct spi_device *spi; + struct mutex lock; + struct regulator *vref; + struct clk *clki; + u32 dev_addr; +}; + +static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) +{ + int ret; + + reg = MCP3911_REG_READ(reg, adc->dev_addr); + ret = spi_write_then_read(adc->spi, &reg, 1, val, len); + if (ret < 0) + return ret; + + be32_to_cpus(val); + *val >>= ((4 - len) * 8); + dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val, + reg >> 1); + return ret; +} + +static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len) +{ + dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg); + + val <<= (3 - len) * 8; + cpu_to_be32s(&val); + val |= MCP3911_REG_WRITE(reg, adc->dev_addr); + + return spi_write(adc->spi, &val, len + 1); +} + +static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, + u32 val, u8 len) +{ + u32 tmp; + int ret; + + ret = mcp3911_read(adc, reg, &tmp, len); + if (ret) + return ret; + + val &= mask; + val |= tmp & ~mask; + return mcp3911_write(adc, reg, val, len); +} + +static int mcp3911_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp3911 *adc = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&adc->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp3911_read(adc, + MCP3911_CHANNEL(channel->channel), val, 3); + if (ret) + goto out; + + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_OFFSET: + ret = mcp3911_read(adc, + MCP3911_OFFCAL(channel->channel), val, 3); + if (ret) + goto out; + + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + if (adc->vref) { + ret = regulator_get_voltage(adc->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to get vref voltage: %d\n", + ret); + goto out; + } + + *val = ret / 1000; + } else { + *val = MCP3911_INT_VREF_UV; + } + + *val2 = 24; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + } + +out: + mutex_unlock(&adc->lock); + return ret; +} + +static int mcp3911_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mcp3911 *adc = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&adc->lock); + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + if (val2 != 0) { + ret = -EINVAL; + goto out; + } + + /* Write offset */ + ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val, + 3); + if (ret) + goto out; + + /* Enable offset*/ + ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, + MCP3911_STATUSCOM_EN_OFFCAL, + MCP3911_STATUSCOM_EN_OFFCAL, 2); + break; + } + +out: + mutex_unlock(&adc->lock); + return ret; +} + +#define MCP3911_CHAN(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = idx, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mcp3911_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), +}; + +static const struct iio_info mcp3911_info = { + .read_raw = mcp3911_read_raw, + .write_raw = mcp3911_write_raw, +}; + +static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node) +{ + u32 configreg; + int ret; + + of_property_read_u32(of_node, "device-addr", &adc->dev_addr); + if (adc->dev_addr > 3) { + dev_err(&adc->spi->dev, + "invalid device address (%i). Must be in range 0-3.\n", + adc->dev_addr); + return -EINVAL; + } + dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); + + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2); + if (ret) + return ret; + + if (adc->vref) { + dev_dbg(&adc->spi->dev, "use external voltage reference\n"); + configreg |= MCP3911_CONFIG_VREFEXT; + } else { + dev_dbg(&adc->spi->dev, + "use internal voltage reference (1.2V)\n"); + configreg &= ~MCP3911_CONFIG_VREFEXT; + } + + if (adc->clki) { + dev_dbg(&adc->spi->dev, "use external clock as clocksource\n"); + configreg |= MCP3911_CONFIG_CLKEXT; + } else { + dev_dbg(&adc->spi->dev, + "use crystal oscillator as clocksource\n"); + configreg &= ~MCP3911_CONFIG_CLKEXT; + } + + return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2); +} + +static int mcp3911_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp3911 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref"); + if (IS_ERR(adc->vref)) { + if (PTR_ERR(adc->vref) == -ENODEV) { + adc->vref = NULL; + } else { + dev_err(&adc->spi->dev, + "failed to get regulator (%ld)\n", + PTR_ERR(adc->vref)); + return PTR_ERR(adc->vref); + } + + } else { + ret = regulator_enable(adc->vref); + if (ret) + return ret; + } + + adc->clki = devm_clk_get(&adc->spi->dev, NULL); + if (IS_ERR(adc->clki)) { + if (PTR_ERR(adc->clki) == -ENOENT) { + adc->clki = NULL; + } else { + dev_err(&adc->spi->dev, + "failed to get adc clk (%ld)\n", + PTR_ERR(adc->clki)); + ret = PTR_ERR(adc->clki); + goto reg_disable; + } + } else { + ret = clk_prepare_enable(adc->clki); + if (ret < 0) { + dev_err(&adc->spi->dev, + "Failed to enable clki: %d\n", ret); + goto reg_disable; + } + } + + ret = mcp3911_config(adc, spi->dev.of_node); + if (ret) + goto clk_disable; + + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp3911_info; + spi_set_drvdata(spi, indio_dev); + + indio_dev->channels = mcp3911_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels); + + mutex_init(&adc->lock); + + ret = iio_device_register(indio_dev); + if (ret) + goto clk_disable; + + return ret; + +clk_disable: + clk_disable_unprepare(adc->clki); +reg_disable: + if (adc->vref) + regulator_disable(adc->vref); + + return ret; +} + +static int mcp3911_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct mcp3911 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + clk_disable_unprepare(adc->clki); + if (adc->vref) + regulator_disable(adc->vref); + + return 0; +} + +static const struct of_device_id mcp3911_dt_ids[] = { + { .compatible = "microchip,mcp3911" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); + +static const struct spi_device_id mcp3911_id[] = { + { "mcp3911", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp3911_id); + +static struct spi_driver mcp3911_driver = { + .driver = { + .name = "mcp3911", + .of_match_table = mcp3911_dt_ids, + }, + .probe = mcp3911_probe, + .remove = mcp3911_remove, + .id_table = mcp3911_id, +}; +module_spi_driver(mcp3911_driver); + +MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); +MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>"); +MODULE_DESCRIPTION("Microchip Technology MCP3911"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c @@ -148,7 +148,6 @@ #define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26) #define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16) #define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15) - #define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11 #define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11) #define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10) #define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0) @@ -173,6 +172,7 @@ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = _chan, \ + .address = _chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ @@ -235,7 +235,7 @@ struct meson_sar_adc_data { struct meson_sar_adc_priv { struct regmap *regmap; struct regulator *vref; - const struct meson_sar_adc_data *data; + const struct meson_sar_adc_param *param; struct clk *clkin; struct clk *core_clk; struct clk *adc_sel_clk; @@ -280,7 +280,7 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) /* use val_calib = scale * val_raw + offset calibration function */ tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; - return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1); + return clamp(tmp, 0, (1 << priv->param->resolution) - 1); } static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) @@ -324,15 +324,15 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &regval); fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval); - if (fifo_chan != chan->channel) { + if (fifo_chan != chan->address) { dev_err(&indio_dev->dev, - "ADC FIFO entry belongs to channel %d instead of %d\n", - fifo_chan, chan->channel); + "ADC FIFO entry belongs to channel %d instead of %lu\n", + fifo_chan, chan->address); return -EINVAL; } fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); - fifo_val &= GENMASK(priv->data->param->resolution - 1, 0); + fifo_val &= GENMASK(priv->param->resolution - 1, 0); *val = meson_sar_adc_calib_val(indio_dev, fifo_val); return 0; @@ -344,16 +344,16 @@ static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev, enum meson_sar_adc_num_samples samples) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - int val, channel = chan->channel; + int val, address = chan->address; - val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel); + val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address); regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, - MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel), + MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address), val); - val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel); + val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address); regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, - MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val); + MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val); } static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, @@ -373,23 +373,23 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, /* map channel index 0 to the channel which we want to read */ regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), - chan->channel); + chan->address); regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval); regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, - chan->channel); + chan->address); regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, regval); regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, - chan->channel); + chan->address); regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, regval); - if (chan->channel == 6) + if (chan->address == 6) regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0); } @@ -451,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev) mutex_lock(&indio_dev->mlock); - if (priv->data->param->has_bl30_integration) { + if (priv->param->has_bl30_integration) { /* prevent BL30 from using the SAR ADC while we are using it */ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, MESON_SAR_ADC_DELAY_KERNEL_BUSY, @@ -479,7 +479,7 @@ static void meson_sar_adc_unlock(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - if (priv->data->param->has_bl30_integration) + if (priv->param->has_bl30_integration) /* allow BL30 to use the SAR ADC again */ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0); @@ -527,8 +527,8 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev, if (ret) { dev_warn(indio_dev->dev.parent, - "failed to read sample for channel %d: %d\n", - chan->channel, ret); + "failed to read sample for channel %lu: %d\n", + chan->address, ret); return ret; } @@ -563,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, } *val = ret / 1000; - *val2 = priv->data->param->resolution; + *val2 = priv->param->resolution; return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_CALIBBIAS: @@ -636,7 +636,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) */ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); - if (priv->data->param->has_bl30_integration) { + if (priv->param->has_bl30_integration) { /* * leave sampling delay and the input clocks as configured by * BL30 to make sure BL30 gets the values it expects when @@ -716,7 +716,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) return ret; } - ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate); + ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate); if (ret) { dev_err(indio_dev->dev.parent, "failed to set adc clock rate\n"); @@ -729,7 +729,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - const struct meson_sar_adc_param *param = priv->data->param; + const struct meson_sar_adc_param *param = priv->param; u32 enable_mask; if (param->bandgap_reg == MESON_SAR_ADC_REG11) @@ -849,13 +849,13 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev) int ret, nominal0, nominal1, value0, value1; /* use points 25% and 75% for calibration */ - nominal0 = (1 << priv->data->param->resolution) / 4; - nominal1 = (1 << priv->data->param->resolution) * 3 / 4; + nominal0 = (1 << priv->param->resolution) / 4; + nominal1 = (1 << priv->param->resolution) * 3 / 4; meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); usleep_range(10, 20); ret = meson_sar_adc_get_sample(indio_dev, - &meson_sar_adc_iio_channels[7], + &indio_dev->channels[7], MEAN_AVERAGING, EIGHT_SAMPLES, &value0); if (ret < 0) goto out; @@ -863,7 +863,7 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev) meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4); usleep_range(10, 20); ret = meson_sar_adc_get_sample(indio_dev, - &meson_sar_adc_iio_channels[7], + &indio_dev->channels[7], MEAN_AVERAGING, EIGHT_SAMPLES, &value1); if (ret < 0) goto out; @@ -979,11 +979,11 @@ MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match); static int meson_sar_adc_probe(struct platform_device *pdev) { + const struct meson_sar_adc_data *match_data; struct meson_sar_adc_priv *priv; struct iio_dev *indio_dev; struct resource *res; void __iomem *base; - const struct of_device_id *match; int irq, ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); @@ -995,15 +995,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev) priv = iio_priv(indio_dev); init_completion(&priv->done); - match = of_match_device(meson_sar_adc_of_match, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "failed to match device\n"); + match_data = of_device_get_match_data(&pdev->dev); + if (!match_data) { + dev_err(&pdev->dev, "failed to get match data\n"); return -ENODEV; } - priv->data = match->data; + priv->param = match_data->param; - indio_dev->name = priv->data->name; + indio_dev->name = match_data->name; indio_dev->dev.parent = &pdev->dev; indio_dev->dev.of_node = pdev->dev.of_node; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1027,7 +1027,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) return ret; priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, - priv->data->param->regmap_config); + priv->param->regmap_config); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -708,8 +708,8 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev, * mux. */ if (iiospec->args_count != 2) { - dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n", - iiospec->np->name, + dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n", + iiospec->np, iiospec->args_count); return -EINVAL; } diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <dt-bindings/iio/qcom,spmi-vadc.h> +#include "qcom-vadc-common.h" + +#define ADC5_USR_REVISION1 0x0 +#define ADC5_USR_STATUS1 0x8 +#define ADC5_USR_STATUS1_REQ_STS BIT(1) +#define ADC5_USR_STATUS1_EOC BIT(0) +#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define ADC5_USR_STATUS2 0x9 +#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70 +#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5 + +#define ADC5_USR_IBAT_MEAS 0xf +#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0) + +#define ADC5_USR_DIG_PARAM 0x42 +#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6) +#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6 +#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30 +#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4 +#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc +#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2 + +#define ADC5_USR_FAST_AVG_CTL 0x43 +#define ADC5_USR_FAST_AVG_CTL_EN BIT(7) +#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7 + +#define ADC5_USR_CH_SEL_CTL 0x44 + +#define ADC5_USR_DELAY_CTL 0x45 +#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf + +#define ADC5_USR_EN_CTL1 0x46 +#define ADC5_USR_EN_CTL1_ADC_EN BIT(7) + +#define ADC5_USR_CONV_REQ 0x47 +#define ADC5_USR_CONV_REQ_REQ BIT(7) + +#define ADC5_USR_DATA0 0x50 + +#define ADC5_USR_DATA1 0x51 + +#define ADC5_USR_IBAT_DATA0 0x52 + +#define ADC5_USR_IBAT_DATA1 0x53 + +/* + * Conversion time varies based on the decimation, clock rate, fast average + * samples and measurements queued across different VADC peripherals. + * Set the timeout to a max of 100ms. + */ +#define ADC5_CONV_TIME_MIN_US 263 +#define ADC5_CONV_TIME_MAX_US 264 +#define ADC5_CONV_TIME_RETRY 400 +#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100) + +/* Digital version >= 5.3 supports hw_settle_2 */ +#define ADC5_HW_SETTLE_DIFF_MINOR 3 +#define ADC5_HW_SETTLE_DIFF_MAJOR 5 + +enum adc5_cal_method { + ADC5_NO_CAL = 0, + ADC5_RATIOMETRIC_CAL, + ADC5_ABSOLUTE_CAL +}; + +enum adc5_cal_val { + ADC5_TIMER_CAL = 0, + ADC5_NEW_CAL +}; + +/** + * struct adc5_channel_prop - ADC channel property. + * @channel: channel number, refer to the channel list. + * @cal_method: calibration method. + * @cal_val: calibration value + * @decimation: sampling rate supported for the channel. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time: the time between AMUX being configured and the + * start of conversion. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + * @datasheet_name: Channel name used in device tree. + */ +struct adc5_channel_prop { + unsigned int channel; + enum adc5_cal_method cal_method; + enum adc5_cal_val cal_val; + unsigned int decimation; + unsigned int prescale; + unsigned int hw_settle_time; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; + const char *datasheet_name; +}; + +/** + * struct adc5_chip - ADC private structure. + * @regmap: SPMI ADC5 peripheral register map field. + * @dev: SPMI ADC5 device. + * @base: base address for the ADC peripheral. + * @nchannels: number of ADC channels. + * @chan_props: array of ADC channel properties. + * @iio_chans: array of IIO channels specification. + * @poll_eoc: use polling instead of interrupt. + * @complete: ADC result notification after interrupt is received. + * @lock: ADC lock for access to the peripheral. + * @data: software configuration data. + */ +struct adc5_chip { + struct regmap *regmap; + struct device *dev; + u16 base; + unsigned int nchannels; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + bool poll_eoc; + struct completion complete; + struct mutex lock; + const struct adc5_data *data; +}; + +static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { + {.num = 1, .den = 1}, + {.num = 1, .den = 3}, + {.num = 1, .den = 4}, + {.num = 1, .den = 6}, + {.num = 1, .den = 20}, + {.num = 1, .den = 8}, + {.num = 10, .den = 81}, + {.num = 1, .den = 10}, + {.num = 1, .den = 16} +}; + +static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc->regmap, adc->base + offset, data, len); +} + +static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc->regmap, adc->base + offset, data, len); +} + +static int adc5_prescaling_from_dt(u32 num, u32 den) +{ + unsigned int pre; + + for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++) + if (adc5_prescale_ratios[pre].num == num && + adc5_prescale_ratios[pre].den == den) + break; + + if (pre == ARRAY_SIZE(adc5_prescale_ratios)) + return -EINVAL; + + return pre; +} + +static int adc5_hw_settle_time_from_dt(u32 value, + const unsigned int *hw_settle) +{ + unsigned int i; + + for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) { + if (value == hw_settle[i]) + return i; + } + + return -EINVAL; +} + +static int adc5_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs(value); +} + +static int adc5_decimation_from_dt(u32 value, + const unsigned int *decimation) +{ + unsigned int i; + + for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) { + if (value == decimation[i]) + return i; + } + + return -EINVAL; +} + +static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data) +{ + int ret; + u8 rslt_lsb, rslt_msb; + + ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb)); + if (ret) + return ret; + + ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb)); + if (ret) + return ret; + + *data = (rslt_msb << 8) | rslt_lsb; + + if (*data == ADC5_USR_DATA_CHECK) { + pr_err("Invalid data:0x%x\n", *data); + return -EINVAL; + } + + pr_debug("voltage raw code:0x%x\n", *data); + + return 0; +} + +static int adc5_poll_wait_eoc(struct adc5_chip *adc) +{ + unsigned int count, retry = ADC5_CONV_TIME_RETRY; + u8 status1; + int ret; + + for (count = 0; count < retry; count++) { + ret = adc5_read(adc, ADC5_USR_STATUS1, &status1, + sizeof(status1)); + if (ret) + return ret; + + status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK; + if (status1 == ADC5_USR_STATUS1_EOC) + return 0; + + usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US); + } + + return -ETIMEDOUT; +} + +static void adc5_update_dig_param(struct adc5_chip *adc, + struct adc5_channel_prop *prop, u8 *data) +{ + /* Update calibration value */ + *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL; + *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT); + + /* Update calibration select */ + *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL; + *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT); + + /* Update decimation ratio select */ + *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL; + *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT); +} + +static int adc5_configure(struct adc5_chip *adc, + struct adc5_channel_prop *prop) +{ + int ret; + u8 buf[6]; + + /* Read registers 0x42 through 0x46 */ + ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret < 0) + return ret; + + /* Digital param selection */ + adc5_update_dig_param(adc, prop, &buf[0]); + + /* Update fast average sample value */ + buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK; + buf[1] |= prop->avg_samples; + + /* Select ADC channel */ + buf[2] = prop->channel; + + /* Select HW settle delay for channel */ + buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK; + buf[3] |= prop->hw_settle_time; + + /* Select ADC enable */ + buf[4] |= ADC5_USR_EN_CTL1_ADC_EN; + + /* Select CONV request */ + buf[5] |= ADC5_USR_CONV_REQ_REQ; + + if (!adc->poll_eoc) + reinit_completion(&adc->complete); + + return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); +} + +static int adc5_do_conversion(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct iio_chan_spec const *chan, + u16 *data_volt, u16 *data_cur) +{ + int ret; + + mutex_lock(&adc->lock); + + ret = adc5_configure(adc, prop); + if (ret) { + pr_err("ADC configure failed with %d\n", ret); + goto unlock; + } + + if (adc->poll_eoc) { + ret = adc5_poll_wait_eoc(adc); + if (ret < 0) { + pr_err("EOC bit not set\n"); + goto unlock; + } + } else { + ret = wait_for_completion_timeout(&adc->complete, + ADC5_CONV_TIMEOUT); + if (!ret) { + pr_debug("Did not get completion timeout.\n"); + ret = adc5_poll_wait_eoc(adc); + if (ret < 0) { + pr_err("EOC bit not set\n"); + goto unlock; + } + } + } + + ret = adc5_read_voltage_data(adc, data_volt); +unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +static irqreturn_t adc5_isr(int irq, void *dev_id) +{ + struct adc5_chip *adc = dev_id; + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static int adc5_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i; + + for (i = 0; i < adc->nchannels; i++) + if (adc->chan_props[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static int adc5_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_prop *prop; + u16 adc_code_volt, adc_code_cur; + int ret; + + prop = &adc->chan_props[chan->address]; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = adc5_do_conversion(adc, prop, chan, + &adc_code_volt, &adc_code_cur); + if (ret) + return ret; + + ret = qcom_adc5_hw_scale(prop->scale_fn_type, + &adc5_prescale_ratios[prop->prescale], + adc->data, + adc_code_volt, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info adc5_info = { + .read_raw = adc5_read_raw, + .of_xlate = adc5_of_xlate, +}; + +struct adc5_channels { + const char *datasheet_name; + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \ + { \ + .datasheet_name = _dname, \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale, \ + }, \ + +#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \ + ADC5_CHAN(_dname, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \ + ADC5_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1, + SCALE_HW_CALIB_PMIC_THERM) + [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 16, + SCALE_HW_CALIB_DEFAULT) + [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 1, + SCALE_HW_CALIB_PM5_CHG_TEMP) + /* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */ + [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 6, + SCALE_HW_CALIB_DEFAULT) + [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 1, + SCALE_HW_CALIB_XOTHERM) + [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 1, + SCALE_HW_CALIB_PM5_SMB_TEMP) +}; + +static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { + [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1, + SCALE_HW_CALIB_PMIC_THERM) + [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) +}; + +static int adc5_get_dt_channel_data(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct device_node *node, + const struct adc5_data *data) +{ + const char *name = node->name, *channel_name; + u32 chan, value, varr[2]; + int ret; + struct device *dev = adc->dev; + + ret = of_property_read_u32(node, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + + if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA || + !data->adc_chans[chan].datasheet_name) { + dev_err(dev, "%s invalid channel number %d\n", name, chan); + return -EINVAL; + } + + /* the channel has DT description */ + prop->channel = chan; + + channel_name = of_get_property(node, + "label", NULL) ? : node->name; + if (!channel_name) { + pr_err("Invalid channel name\n"); + return -EINVAL; + } + prop->datasheet_name = channel_name; + + ret = of_property_read_u32(node, "qcom,decimation", &value); + if (!ret) { + ret = adc5_decimation_from_dt(value, data->decimation); + if (ret < 0) { + dev_err(dev, "%02x invalid decimation %d\n", + chan, value); + return ret; + } + prop->decimation = ret; + } else { + prop->decimation = ADC5_DECIMATION_DEFAULT; + } + + ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) { + dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + return ret; + } + prop->prescale = ret; + } + + ret = of_property_read_u32(node, "qcom,hw-settle-time", &value); + if (!ret) { + u8 dig_version[2]; + + ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version, + sizeof(dig_version)); + if (ret < 0) { + dev_err(dev, "Invalid dig version read %d\n", ret); + return ret; + } + + pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0], + dig_version[1]); + /* Digital controller >= 5.3 have hw_settle_2 option */ + if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR && + dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) + ret = adc5_hw_settle_time_from_dt(value, + data->hw_settle_2); + else + ret = adc5_hw_settle_time_from_dt(value, + data->hw_settle_1); + + if (ret < 0) { + dev_err(dev, "%02x invalid hw-settle-time %d us\n", + chan, value); + return ret; + } + prop->hw_settle_time = ret; + } else { + prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; + } + + ret = of_property_read_u32(node, "qcom,avg-samples", &value); + if (!ret) { + ret = adc5_avg_samples_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid avg-samples %d\n", + chan, value); + return ret; + } + prop->avg_samples = ret; + } else { + prop->avg_samples = VADC_DEF_AVG_SAMPLES; + } + + if (of_property_read_bool(node, "qcom,ratiometric")) + prop->cal_method = ADC5_RATIOMETRIC_CAL; + else + prop->cal_method = ADC5_ABSOLUTE_CAL; + + /* + * Default to using timer calibration. Using a fresh calibration value + * for every conversion will increase the overall time for a request. + */ + prop->cal_val = ADC5_TIMER_CAL; + + dev_dbg(dev, "%02x name %s\n", chan, name); + + return 0; +} + +static const struct adc5_data adc5_data_pmic = { + .full_scale_code_volt = 0x70e4, + .full_scale_code_cur = 0x2710, + .adc_chans = adc5_chans_pmic, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {250, 420, 840}, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static const struct adc5_data adc5_data_pmic_rev2 = { + .full_scale_code_volt = 0x4000, + .full_scale_code_cur = 0x1800, + .adc_chans = adc5_chans_rev2, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {256, 512, 1024}, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {0, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static const struct of_device_id adc5_match_table[] = { + { + .compatible = "qcom,spmi-adc5", + .data = &adc5_data_pmic, + }, + { + .compatible = "qcom,spmi-adc-rev2", + .data = &adc5_data_pmic_rev2, + }, + { } +}; + +static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) +{ + const struct adc5_channels *adc_chan; + struct iio_chan_spec *iio_chan; + struct adc5_channel_prop prop, *chan_props; + struct device_node *child; + unsigned int index = 0; + const struct of_device_id *id; + const struct adc5_data *data; + int ret; + + adc->nchannels = of_get_available_child_count(node); + if (!adc->nchannels) + return -EINVAL; + + adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels, + sizeof(*adc->chan_props), GFP_KERNEL); + if (!adc->chan_props) + return -ENOMEM; + + chan_props = adc->chan_props; + iio_chan = adc->iio_chans; + id = of_match_node(adc5_match_table, node); + if (id) + data = id->data; + else + data = &adc5_data_pmic; + adc->data = data; + + for_each_available_child_of_node(node, child) { + ret = adc5_get_dt_channel_data(adc, &prop, child, data); + if (ret) { + of_node_put(child); + return ret; + } + + prop.scale_fn_type = + data->adc_chans[prop.channel].scale_fn_type; + *chan_props = prop; + adc_chan = &data->adc_chans[prop.channel]; + + iio_chan->channel = prop.channel; + iio_chan->datasheet_name = prop.datasheet_name; + iio_chan->extend_name = prop.datasheet_name; + iio_chan->info_mask_separate = adc_chan->info_mask; + iio_chan->type = adc_chan->type; + iio_chan->address = index; + iio_chan++; + chan_props++; + index++; + } + + return 0; +} + +static int adc5_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adc5_chip *adc; + struct regmap *regmap; + int ret, irq_eoc; + u32 reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", &reg); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->regmap = regmap; + adc->dev = dev; + adc->base = reg; + init_completion(&adc->complete); + mutex_init(&adc->lock); + + ret = adc5_get_dt_data(adc, node); + if (ret) { + pr_err("adc get dt data failed\n"); + return ret; + } + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc < 0) { + if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL) + return irq_eoc; + adc->poll_eoc = true; + } else { + ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0, + "pm-adc5", adc); + if (ret) + return ret; + } + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = node; + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc5_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver adc5_driver = { + .driver = { + .name = "qcom-spmi-adc5.c", + .of_match_table = adc5_match_table, + }, + .probe = adc5_probe, +}; +module_platform_driver(adc5_driver); + +MODULE_ALIAS("platform:qcom-spmi-adc5"); +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c @@ -47,8 +47,79 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { {44, 125} }; +/* + * Voltage to temperature table for 100k pull up for NTCG104EF104 with + * 1.875V reference. + */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { + { 1831, -40000 }, + { 1814, -35000 }, + { 1791, -30000 }, + { 1761, -25000 }, + { 1723, -20000 }, + { 1675, -15000 }, + { 1616, -10000 }, + { 1545, -5000 }, + { 1463, 0 }, + { 1370, 5000 }, + { 1268, 10000 }, + { 1160, 15000 }, + { 1049, 20000 }, + { 937, 25000 }, + { 828, 30000 }, + { 726, 35000 }, + { 630, 40000 }, + { 544, 45000 }, + { 467, 50000 }, + { 399, 55000 }, + { 340, 60000 }, + { 290, 65000 }, + { 247, 70000 }, + { 209, 75000 }, + { 179, 80000 }, + { 153, 85000 }, + { 130, 90000 }, + { 112, 95000 }, + { 96, 100000 }, + { 82, 105000 }, + { 71, 110000 }, + { 62, 115000 }, + { 53, 120000 }, + { 46, 125000 }, +}; + +static int qcom_vadc_scale_hw_calib_volt( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv); +static int qcom_vadc_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_chg5_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); + +static struct qcom_adc5_scale_type scale_adc5_fn[] = { + [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt}, + [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp}, + [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp}, + [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp}, +}; + static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, - u32 tablesize, s32 input, s64 *output) + u32 tablesize, s32 input, int *output) { bool descending = 1; u32 i = 0; @@ -128,7 +199,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, bool absolute, u16 adc_code, int *result_mdec) { - s64 voltage = 0, result = 0; + s64 voltage = 0; int ret; qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); @@ -138,12 +209,11 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), - voltage, &result); + voltage, result_mdec); if (ret) return ret; - result *= 1000; - *result_mdec = result; + *result_mdec *= 1000; return 0; } @@ -191,6 +261,99 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, return 0; } +static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + unsigned int factor) +{ + s64 voltage, temp, adc_vdd_ref_mv = 1875; + + /* + * The normal data range is between 0V to 1.875V. On cases where + * we read low voltage values, the ADC code can go beyond the + * range and the scale result is incorrect so we clamp the values + * for the cases where the code represents a value below 0V + */ + if (adc_code > VADC5_MAX_CODE) + adc_code = 0; + + /* (ADC code * vref_vadc (1.875V)) / full_scale_code */ + voltage = (s64) adc_code * adc_vdd_ref_mv * 1000; + voltage = div64_s64(voltage, data->full_scale_code_volt); + if (voltage > 0) { + voltage *= prescale->den; + temp = prescale->num * factor; + voltage = div64_s64(voltage, temp); + } else { + voltage = 0; + } + + return (int) voltage; +} + +static int qcom_vadc_scale_hw_calib_volt( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv) +{ + *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1); + + return 0; +} + +static int qcom_vadc_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + int voltage; + + voltage = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1000); + + /* Map voltage to temperature from look-up table */ + return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + voltage, result_mdec); +} + +static int qcom_vadc_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 2); + *result_mdec -= KELVINMIL_CELSIUSMIL; + + return 0; +} + +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100, + prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR); + *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec; + + return 0; +} + +static int qcom_vadc_scale_hw_chg5_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 4); + *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec; + + return 0; +} + int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, const struct vadc_linear_graph *calib_graph, const struct vadc_prescale_ratio *prescale, @@ -221,6 +384,22 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, } EXPORT_SYMBOL(qcom_vadc_scale); +int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result) +{ + if (!(scaletype >= SCALE_HW_CALIB_DEFAULT && + scaletype < SCALE_HW_CALIB_INVALID)) { + pr_err("Invalid scale type %d\n", scaletype); + return -EINVAL; + } + + return scale_adc5_fn[scaletype].scale_fn(prescale, data, + adc_code, result); +} +EXPORT_SYMBOL(qcom_adc5_hw_scale); + int qcom_vadc_decimation_from_dt(u32 value) { if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h @@ -25,15 +25,31 @@ #define VADC_DECIMATION_MIN 512 #define VADC_DECIMATION_MAX 4096 +#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */ +#define ADC5_DECIMATION_SHORT 250 +#define ADC5_DECIMATION_MEDIUM 420 +#define ADC5_DECIMATION_LONG 840 +/* Default decimation - 1024 for rev2, 840 for pmic5 */ +#define ADC5_DECIMATION_DEFAULT 2 +#define ADC5_DECIMATION_SAMPLES_MAX 3 #define VADC_HW_SETTLE_DELAY_MAX 10000 +#define VADC_HW_SETTLE_SAMPLES_MAX 16 #define VADC_AVG_SAMPLES_MAX 512 +#define ADC5_AVG_SAMPLES_MAX 16 #define KELVINMIL_CELSIUSMIL 273150 +#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500 +#define PMIC5_SMB_TEMP_CONSTANT 419400 +#define PMIC5_SMB_TEMP_SCALE_FACTOR 356 #define PMI_CHG_SCALE_1 -138890 #define PMI_CHG_SCALE_2 391750000000LL +#define VADC5_MAX_CODE 0x7fff +#define ADC5_FULL_SCALE_CODE 0x70e4 +#define ADC5_USR_DATA_CHECK 0x8000 + /** * struct vadc_map_pt - Map the graph representation for ADC channel * @x: Represent the ADC digitized code. @@ -89,6 +105,18 @@ struct vadc_prescale_ratio { * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade. * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC. * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp + * SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to + * voltage (uV) with hardware applied offset/slope values to adc code. + * SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using + * lookup table. The hardware applies offset/slope to adc code. + * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using + * 100k pullup. The hardware applies offset/slope to adc code. + * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade. + * The hardware applies offset/slope to adc code. + * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5 + * charger temperature. + * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5 + * SMB1390 temperature. */ enum vadc_scale_fn_type { SCALE_DEFAULT = 0, @@ -96,6 +124,22 @@ enum vadc_scale_fn_type { SCALE_PMIC_THERM, SCALE_XOTHERM, SCALE_PMI_CHG_TEMP, + SCALE_HW_CALIB_DEFAULT, + SCALE_HW_CALIB_THERM_100K_PULLUP, + SCALE_HW_CALIB_XOTHERM, + SCALE_HW_CALIB_PMIC_THERM, + SCALE_HW_CALIB_PM5_CHG_TEMP, + SCALE_HW_CALIB_PM5_SMB_TEMP, + SCALE_HW_CALIB_INVALID, +}; + +struct adc5_data { + const u32 full_scale_code_volt; + const u32 full_scale_code_cur; + const struct adc5_channels *adc_chans; + unsigned int *decimation; + unsigned int *hw_settle_1; + unsigned int *hw_settle_2; }; int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, @@ -104,6 +148,16 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, bool absolute, u16 adc_code, int *result_mdec); +struct qcom_adc5_scale_type { + int (*scale_fn)(const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, u16 adc_code, int *result); +}; + +int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); + int qcom_vadc_decimation_from_dt(u32 value); #endif /* QCOM_VADC_COMMON_H */ diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c @@ -343,8 +343,8 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) for_each_child_of_node(np, child) { of_id = of_match_node(rcar_gyroadc_child_match, child); if (!of_id) { - dev_err(dev, "Ignoring unsupported ADC \"%s\".", - child->name); + dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".", + child); continue; } @@ -381,16 +381,16 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) ret = of_property_read_u32(child, "reg", &reg); if (ret) { dev_err(dev, - "Failed to get child reg property of ADC \"%s\".\n", - child->name); + "Failed to get child reg property of ADC \"%pOFn\".\n", + child); return ret; } /* Channel number is too high. */ if (reg >= num_channels) { dev_err(dev, - "Only %i channels supported with %s, but reg = <%i>.\n", - num_channels, child->name, reg); + "Only %i channels supported with %pOFn, but reg = <%i>.\n", + num_channels, child, reg); return ret; } } diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c @@ -5,10 +5,12 @@ #include <linux/iio/iio.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/nvmem-consumer.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/slab.h> /* PMIC global registers definition */ #define SC27XX_MODULE_EN 0xc08 @@ -87,16 +89,73 @@ struct sc27xx_adc_linear_graph { * should use the small-scale graph, and if more than 1.2v, we should use the * big-scale graph. */ -static const struct sc27xx_adc_linear_graph big_scale_graph = { +static struct sc27xx_adc_linear_graph big_scale_graph = { 4200, 3310, 3600, 2832, }; -static const struct sc27xx_adc_linear_graph small_scale_graph = { +static struct sc27xx_adc_linear_graph small_scale_graph = { 1000, 3413, 100, 341, }; +static const struct sc27xx_adc_linear_graph big_scale_graph_calib = { + 4200, 856, + 3600, 733, +}; + +static const struct sc27xx_adc_linear_graph small_scale_graph_calib = { + 1000, 833, + 100, 80, +}; + +static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc) +{ + return ((calib_data & 0xff) + calib_adc - 128) * 4; +} + +static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data, + bool big_scale) +{ + const struct sc27xx_adc_linear_graph *calib_graph; + struct sc27xx_adc_linear_graph *graph; + struct nvmem_cell *cell; + const char *cell_name; + u32 calib_data = 0; + void *buf; + size_t len; + + if (big_scale) { + calib_graph = &big_scale_graph_calib; + graph = &big_scale_graph; + cell_name = "big_scale_calib"; + } else { + calib_graph = &small_scale_graph_calib; + graph = &small_scale_graph; + cell_name = "small_scale_calib"; + } + + cell = nvmem_cell_get(data->dev, cell_name); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + memcpy(&calib_data, buf, min(len, sizeof(u32))); + + /* Only need to calibrate the adc values in the linear graph. */ + graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0); + graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8, + calib_graph->adc1); + + kfree(buf); + return 0; +} + static int sc27xx_adc_get_ratio(int channel, int scale) { switch (channel) { @@ -209,7 +268,7 @@ static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, *div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK; } -static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph, +static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, int raw_adc) { int tmp; @@ -273,6 +332,17 @@ static int sc27xx_adc_read_raw(struct iio_dev *indio_dev, int ret, tmp; switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = sc27xx_adc_read(data, chan->channel, scale, &tmp); + mutex_unlock(&indio_dev->mlock); + + if (ret) + return ret; + + *val = tmp; + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: mutex_lock(&indio_dev->mlock); ret = sc27xx_adc_read_processed(data, chan->channel, scale, @@ -315,48 +385,47 @@ static const struct iio_info sc27xx_info = { .write_raw = &sc27xx_adc_write_raw, }; -#define SC27XX_ADC_CHANNEL(index) { \ +#define SC27XX_ADC_CHANNEL(index, mask) { \ .type = IIO_VOLTAGE, \ .channel = index, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = "CH##index", \ .indexed = 1, \ } static const struct iio_chan_spec sc27xx_channels[] = { - SC27XX_ADC_CHANNEL(0), - SC27XX_ADC_CHANNEL(1), - SC27XX_ADC_CHANNEL(2), - SC27XX_ADC_CHANNEL(3), - SC27XX_ADC_CHANNEL(4), - SC27XX_ADC_CHANNEL(5), - SC27XX_ADC_CHANNEL(6), - SC27XX_ADC_CHANNEL(7), - SC27XX_ADC_CHANNEL(8), - SC27XX_ADC_CHANNEL(9), - SC27XX_ADC_CHANNEL(10), - SC27XX_ADC_CHANNEL(11), - SC27XX_ADC_CHANNEL(12), - SC27XX_ADC_CHANNEL(13), - SC27XX_ADC_CHANNEL(14), - SC27XX_ADC_CHANNEL(15), - SC27XX_ADC_CHANNEL(16), - SC27XX_ADC_CHANNEL(17), - SC27XX_ADC_CHANNEL(18), - SC27XX_ADC_CHANNEL(19), - SC27XX_ADC_CHANNEL(20), - SC27XX_ADC_CHANNEL(21), - SC27XX_ADC_CHANNEL(22), - SC27XX_ADC_CHANNEL(23), - SC27XX_ADC_CHANNEL(24), - SC27XX_ADC_CHANNEL(25), - SC27XX_ADC_CHANNEL(26), - SC27XX_ADC_CHANNEL(27), - SC27XX_ADC_CHANNEL(28), - SC27XX_ADC_CHANNEL(29), - SC27XX_ADC_CHANNEL(30), - SC27XX_ADC_CHANNEL(31), + SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)), + SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)), }; static int sc27xx_adc_enable(struct sc27xx_adc_data *data) @@ -380,6 +449,15 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data) if (ret) goto disable_clk; + /* ADC channel scales' calibration from nvmem device */ + ret = sc27xx_adc_scale_calibration(data, true); + if (ret) + goto disable_clk; + + ret = sc27xx_adc_scale_calibration(data, false); + if (ret) + goto disable_clk; + return 0; disable_clk: diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c @@ -51,7 +51,7 @@ struct ti_ads7950_state { struct spi_device *spi; - struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2]; + struct spi_transfer ring_xfer; struct spi_transfer scan_single_xfer[3]; struct spi_message ring_msg; struct spi_message scan_single_msg; @@ -65,11 +65,11 @@ struct ti_ads7950_state { * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ - __be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE] + u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE] ____cacheline_aligned; - __be16 tx_buf[TI_ADS7950_MAX_CHAN]; - __be16 single_tx; - __be16 single_rx; + u16 tx_buf[TI_ADS7950_MAX_CHAN + 2]; + u16 single_tx; + u16 single_rx; }; @@ -108,7 +108,7 @@ enum ti_ads7950_id { .realbits = bits, \ .storagebits = 16, \ .shift = 12 - (bits), \ - .endianness = IIO_BE, \ + .endianness = IIO_CPU, \ }, \ } @@ -249,23 +249,14 @@ static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev, len = 0; for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) { cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings; - st->tx_buf[len++] = cpu_to_be16(cmd); + st->tx_buf[len++] = cmd; } /* Data for the 1st channel is not returned until the 3rd transfer */ - len += 2; - for (i = 0; i < len; i++) { - if ((i + 2) < len) - st->ring_xfer[i].tx_buf = &st->tx_buf[i]; - if (i >= 2) - st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2]; - st->ring_xfer[i].len = 2; - st->ring_xfer[i].cs_change = 1; - } - /* make sure last transfer's cs_change is not set */ - st->ring_xfer[len - 1].cs_change = 0; + st->tx_buf[len++] = 0; + st->tx_buf[len++] = 0; - spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len); + st->ring_xfer.len = len * 2; return 0; } @@ -281,7 +272,7 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) if (ret < 0) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2], iio_get_time_ns(indio_dev)); out: @@ -298,13 +289,13 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch) mutex_lock(&indio_dev->mlock); cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings; - st->single_tx = cpu_to_be16(cmd); + st->single_tx = cmd; ret = spi_sync(st->spi, &st->scan_single_msg); if (ret) goto out; - ret = be16_to_cpu(st->single_rx); + ret = st->single_rx; out: mutex_unlock(&indio_dev->mlock); @@ -378,6 +369,14 @@ static int ti_ads7950_probe(struct spi_device *spi) const struct ti_ads7950_chip_info *info; int ret; + spi->bits_per_word = 16; + spi->mode |= SPI_CS_WORD; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "Error in spi setup\n"); + return ret; + } + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -398,6 +397,16 @@ static int ti_ads7950_probe(struct spi_device *spi) indio_dev->num_channels = info->num_channels; indio_dev->info = &ti_ads7950_info; + /* build spi ring message */ + spi_message_init(&st->ring_msg); + + st->ring_xfer.tx_buf = &st->tx_buf[0]; + st->ring_xfer.rx_buf = &st->rx_buf[0]; + /* len will be set later */ + st->ring_xfer.cs_change = true; + + spi_message_add_tail(&st->ring_xfer, &st->ring_msg); + /* * Setup default message. The sample is read at the end of the first * transfer, then it takes one full cycle to convert the sample and one diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c @@ -209,6 +209,6 @@ static struct spi_driver ad8366_driver = { module_spi_driver(ad8366_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD8366 VGA"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h @@ -4,10 +4,10 @@ #define BME680_REG_CHIP_I2C_ID 0xD0 #define BME680_REG_CHIP_SPI_ID 0x50 -#define BME680_CHIP_ID_VAL 0x61 +#define BME680_CHIP_ID_VAL 0x61 #define BME680_REG_SOFT_RESET_I2C 0xE0 #define BME680_REG_SOFT_RESET_SPI 0x60 -#define BME680_CMD_SOFTRESET 0xB6 +#define BME680_CMD_SOFTRESET 0xB6 #define BME680_REG_STATUS 0x73 #define BME680_SPI_MEM_PAGE_BIT BIT(4) #define BME680_SPI_MEM_PAGE_1_VAL 1 @@ -18,6 +18,7 @@ #define BME680_REG_GAS_MSB 0x2A #define BME680_REG_GAS_R_LSB 0x2B #define BME680_GAS_STAB_BIT BIT(4) +#define BME680_GAS_RANGE_MASK GENMASK(3, 0) #define BME680_REG_CTRL_HUMIDITY 0x72 #define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0) @@ -26,9 +27,8 @@ #define BME680_OSRS_TEMP_MASK GENMASK(7, 5) #define BME680_OSRS_PRESS_MASK GENMASK(4, 2) #define BME680_MODE_MASK GENMASK(1, 0) - -#define BME680_MODE_FORCED 1 -#define BME680_MODE_SLEEP 0 +#define BME680_MODE_FORCED 1 +#define BME680_MODE_SLEEP 0 #define BME680_REG_CONFIG 0x75 #define BME680_FILTER_MASK GENMASK(4, 2) @@ -39,24 +39,21 @@ #define BME680_MAX_OVERFLOW_VAL 0x40000000 #define BME680_HUM_REG_SHIFT_VAL 4 -#define BME680_BIT_H1_DATA_MSK 0x0F +#define BME680_BIT_H1_DATA_MASK GENMASK(3, 0) #define BME680_REG_RES_HEAT_RANGE 0x02 -#define BME680_RHRANGE_MSK 0x30 +#define BME680_RHRANGE_MASK GENMASK(5, 4) #define BME680_REG_RES_HEAT_VAL 0x00 #define BME680_REG_RANGE_SW_ERR 0x04 -#define BME680_RSERROR_MSK 0xF0 +#define BME680_RSERROR_MASK GENMASK(7, 4) #define BME680_REG_RES_HEAT_0 0x5A #define BME680_REG_GAS_WAIT_0 0x64 -#define BME680_GAS_RANGE_MASK 0x0F #define BME680_ADC_GAS_RES_SHIFT 6 #define BME680_AMB_TEMP 25 #define BME680_REG_CTRL_GAS_1 0x71 #define BME680_RUN_GAS_MASK BIT(4) #define BME680_NB_CONV_MASK GENMASK(3, 0) -#define BME680_RUN_GAS_EN_BIT BIT(4) -#define BME680_NB_CONV_0_VAL 0 #define BME680_REG_MEAS_STAT_0 0x1D #define BME680_GAS_MEAS_BIT BIT(6) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c @@ -91,8 +91,6 @@ static const struct iio_chan_spec bme680_channels[] = { }, }; -static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 }; - static int bme680_read_calib(struct bme680_data *data, struct bme680_calib *calib) { @@ -102,16 +100,14 @@ static int bme680_read_calib(struct bme680_data *data, __le16 buf; /* Temperature related coefficients */ - ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_T1_LSB_REG\n"); return ret; } calib->par_t1 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_T2_LSB_REG\n"); return ret; @@ -126,16 +122,14 @@ static int bme680_read_calib(struct bme680_data *data, calib->par_t3 = tmp; /* Pressure related coefficients */ - ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P1_LSB_REG\n"); return ret; } calib->par_p1 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P2_LSB_REG\n"); return ret; @@ -149,16 +143,14 @@ static int bme680_read_calib(struct bme680_data *data, } calib->par_p3 = tmp; - ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P4_LSB_REG\n"); return ret; } calib->par_p4 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P5_LSB_REG\n"); return ret; @@ -179,16 +171,14 @@ static int bme680_read_calib(struct bme680_data *data, } calib->par_p7 = tmp; - ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P8_LSB_REG\n"); return ret; } calib->par_p8 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P9_LSB_REG\n"); return ret; @@ -208,30 +198,26 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read BME680_H1_MSB_REG\n"); return ret; } - ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb); if (ret < 0) { dev_err(dev, "failed to read BME680_H1_LSB_REG\n"); return ret; } - calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | - (tmp_lsb & BME680_BIT_H1_DATA_MSK); + (tmp_lsb & BME680_BIT_H1_DATA_MASK); ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb); if (ret < 0) { dev_err(dev, "failed to read BME680_H2_MSB_REG\n"); return ret; } - ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb); if (ret < 0) { dev_err(dev, "failed to read BME680_H2_LSB_REG\n"); return ret; } - calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | - (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL); + (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL); ret = regmap_read(data->regmap, BME680_H3_REG, &tmp); if (ret < 0) { @@ -276,8 +262,8 @@ static int bme680_read_calib(struct bme680_data *data, } calib->par_gh1 = tmp; - ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, (u8 *) &buf, + 2); if (ret < 0) { dev_err(dev, "failed to read BME680_GH2_LSB_REG\n"); return ret; @@ -297,7 +283,7 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read resistance heat range\n"); return ret; } - calib->res_heat_range = (tmp & BME680_RHRANGE_MSK) / 16; + calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp); ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp); if (ret < 0) { @@ -311,7 +297,7 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read range software error\n"); return ret; } - calib->range_sw_err = (tmp & BME680_RSERROR_MSK) / 16; + calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp); return 0; } @@ -408,10 +394,7 @@ static u32 bme680_compensate_humid(struct bme680_data *data, var6 = (var4 * var5) >> 1; calc_hum = (((var3 + var6) >> 10) * 1000) >> 12; - if (calc_hum > 100000) /* Cap at 100%rH */ - calc_hum = 100000; - else if (calc_hum < 0) - calc_hum = 0; + calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */ return calc_hum; } @@ -518,12 +501,20 @@ static int bme680_set_mode(struct bme680_data *data, bool mode) return ret; } +static u8 bme680_oversampling_to_reg(u8 val) +{ + return ilog2(val) + 1; +} + static int bme680_chip_config(struct bme680_data *data) { struct device *dev = regmap_get_device(data->regmap); int ret; - u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK, - data->oversampling_humid + 1); + u8 osrs; + + osrs = FIELD_PREP( + BME680_OSRS_HUMIDITY_MASK, + bme680_oversampling_to_reg(data->oversampling_humid)); /* * Highly recommended to set oversampling of humidity before * temperature/pressure oversampling. @@ -544,12 +535,12 @@ static int bme680_chip_config(struct bme680_data *data) return ret; } - osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) | - FIELD_PREP(BME680_OSRS_PRESS_MASK, data->oversampling_press + 1); - + osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, + bme680_oversampling_to_reg(data->oversampling_temp)) | + FIELD_PREP(BME680_OSRS_PRESS_MASK, + bme680_oversampling_to_reg(data->oversampling_press)); ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS, - BME680_OSRS_TEMP_MASK | - BME680_OSRS_PRESS_MASK, + BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK, osrs); if (ret < 0) dev_err(dev, "failed to write ctrl_meas register\n"); @@ -577,14 +568,15 @@ static int bme680_gas_config(struct bme680_data *data) /* set target heating duration */ ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur); if (ret < 0) { - dev_err(dev, "failted to write gas_wait_0 register\n"); + dev_err(dev, "failed to write gas_wait_0 register\n"); return ret; } - /* Selecting the runGas and NB conversion settings for the sensor */ + /* Enable the gas sensor and select heater profile set-point 0 */ ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1, BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK, - BME680_RUN_GAS_EN_BIT | BME680_NB_CONV_0_VAL); + FIELD_PREP(BME680_RUN_GAS_MASK, 1) | + FIELD_PREP(BME680_NB_CONV_MASK, 0)); if (ret < 0) dev_err(dev, "failed to write ctrl_gas_1 register\n"); @@ -782,13 +774,13 @@ static int bme680_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->type) { case IIO_TEMP: - *val = 1 << data->oversampling_temp; + *val = data->oversampling_temp; return IIO_VAL_INT; case IIO_PRESSURE: - *val = 1 << data->oversampling_press; + *val = data->oversampling_press; return IIO_VAL_INT; case IIO_HUMIDITYRELATIVE: - *val = 1 << data->oversampling_humid; + *val = data->oversampling_humid; return IIO_VAL_INT; default: return -EINVAL; @@ -798,52 +790,9 @@ static int bme680_read_raw(struct iio_dev *indio_dev, } } -static int bme680_write_oversampling_ratio_temp(struct bme680_data *data, - int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) { - if (bme680_oversampling_avail[i] == val) { - data->oversampling_temp = ilog2(val); - - return bme680_chip_config(data); - } - } - - return -EINVAL; -} - -static int bme680_write_oversampling_ratio_press(struct bme680_data *data, - int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) { - if (bme680_oversampling_avail[i] == val) { - data->oversampling_press = ilog2(val); - - return bme680_chip_config(data); - } - } - - return -EINVAL; -} - -static int bme680_write_oversampling_ratio_humid(struct bme680_data *data, - int val) +static bool bme680_is_valid_oversampling(int rate) { - int i; - - for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) { - if (bme680_oversampling_avail[i] == val) { - data->oversampling_humid = ilog2(val); - - return bme680_chip_config(data); - } - } - - return -EINVAL; + return (rate > 0 && rate <= 16 && is_power_of_2(rate)); } static int bme680_write_raw(struct iio_dev *indio_dev, @@ -852,18 +801,31 @@ static int bme680_write_raw(struct iio_dev *indio_dev, { struct bme680_data *data = iio_priv(indio_dev); + if (val2 != 0) + return -EINVAL; + switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + { + if (!bme680_is_valid_oversampling(val)) + return -EINVAL; + switch (chan->type) { case IIO_TEMP: - return bme680_write_oversampling_ratio_temp(data, val); + data->oversampling_temp = val; + break; case IIO_PRESSURE: - return bme680_write_oversampling_ratio_press(data, val); + data->oversampling_press = val; + break; case IIO_HUMIDITYRELATIVE: - return bme680_write_oversampling_ratio_humid(data, val); + data->oversampling_humid = val; + break; default: return -EINVAL; } + + return bme680_chip_config(data); + } default: return -EINVAL; } @@ -925,9 +887,9 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, indio_dev->modes = INDIO_DIRECT_MODE; /* default values for the sensor */ - data->oversampling_humid = ilog2(2); /* 2X oversampling rate */ - data->oversampling_press = ilog2(4); /* 4X oversampling rate */ - data->oversampling_temp = ilog2(8); /* 8X oversampling rate */ + data->oversampling_humid = 2; /* 2X oversampling rate */ + data->oversampling_press = 4; /* 4X oversampling rate */ + data->oversampling_temp = 8; /* 8X oversampling rate */ data->heater_temp = 320; /* degree Celsius */ data->heater_dur = 150; /* milliseconds */ diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig @@ -120,6 +120,16 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config LTC1660 + tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Linear Technology + LTC1660 and LTC1665 Digital to Analog Converters. + + To compile this driver as a module, choose M here: the + module will be called ltc1660. + config LTC2632 tristate "Linear Technology LTC2632-12/10/8 DAC spi driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_DS4424) += ds4424.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o +obj-$(CONFIG_LTC1660) += ltc1660.o obj-$(CONFIG_LTC2632) += ltc2632.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c @@ -808,6 +808,40 @@ static int ad5064_set_config(struct ad5064_state *st, unsigned int val) return ad5064_write(st, cmd, 0, val, 0); } +static int ad5064_request_vref(struct ad5064_state *st, struct device *dev) +{ + unsigned int i; + int ret; + + for (i = 0; i < ad5064_num_vref(st); ++i) + st->vref_reg[i].supply = ad5064_vref_name(st, i); + + if (!st->chip_info->internal_vref) + return devm_regulator_bulk_get(dev, ad5064_num_vref(st), + st->vref_reg); + + /* + * This assumes that when the regulator has an internal VREF + * there is only one external VREF connection, which is + * currently the case for all supported devices. + */ + st->vref_reg[0].consumer = devm_regulator_get_optional(dev, "vref"); + if (!IS_ERR(st->vref_reg[0].consumer)) + return 0; + + ret = PTR_ERR(st->vref_reg[0].consumer); + if (ret != -ENODEV) + return ret; + + /* If no external regulator was supplied use the internal VREF */ + st->use_internal_vref = true; + ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE); + if (ret) + dev_err(dev, "Failed to enable internal vref: %d\n", ret); + + return ret; +} + static int ad5064_probe(struct device *dev, enum ad5064_type type, const char *name, ad5064_write_func write) { @@ -828,22 +862,11 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, st->dev = dev; st->write = write; - for (i = 0; i < ad5064_num_vref(st); ++i) - st->vref_reg[i].supply = ad5064_vref_name(st, i); + ret = ad5064_request_vref(st, dev); + if (ret) + return ret; - ret = devm_regulator_bulk_get(dev, ad5064_num_vref(st), - st->vref_reg); - if (ret) { - if (!st->chip_info->internal_vref) - return ret; - st->use_internal_vref = true; - ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE); - if (ret) { - dev_err(dev, "Failed to enable internal vref: %d\n", - ret); - return ret; - } - } else { + if (!st->use_internal_vref) { ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); if (ret) return ret; diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c @@ -628,6 +628,6 @@ static void __exit ad5446_exit(void) } module_exit(ad5446_exit); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c @@ -369,6 +369,6 @@ static struct spi_driver ad5504_driver = { }; module_spi_driver(ad5504_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c @@ -470,6 +470,6 @@ int ad5686_remove(struct device *dev) } EXPORT_SYMBOL_GPL(ad5686_remove); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/property.h> #include <linux/spi/spi.h> +#include <linux/gpio/consumer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -108,6 +109,7 @@ struct ad5758_range { struct ad5758_state { struct spi_device *spi; struct mutex lock; + struct gpio_desc *gpio_reset; struct ad5758_range out_range; unsigned int dc_dc_mode; unsigned int dc_dc_ilim; @@ -474,6 +476,21 @@ static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable) AD5758_CAL_MEM_UNREFRESHED_MSK); } +static int ad5758_reset(struct ad5758_state *st) +{ + if (st->gpio_reset) { + gpiod_set_value(st->gpio_reset, 0); + usleep_range(100, 1000); + gpiod_set_value(st->gpio_reset, 1); + usleep_range(100, 1000); + + return 0; + } else { + /* Perform a software reset */ + return ad5758_soft_reset(st); + } +} + static int ad5758_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, @@ -768,13 +785,18 @@ static int ad5758_init(struct ad5758_state *st) { int regval, ret; + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); + /* Disable CRC checks */ ret = ad5758_crc_disable(st); if (ret < 0) return ret; - /* Perform a software reset */ - ret = ad5758_soft_reset(st); + /* Perform a reset */ + ret = ad5758_reset(st); if (ret < 0) return ret; diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c @@ -467,6 +467,6 @@ static struct spi_driver ad5791_driver = { }; module_spi_driver(ad5791_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * IIO DAC emulation driver using a digital potentiometer * * Copyright (C) 2016 Axentia Technologies AB * * Author: Peter Rosin <peda@axentia.se> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /* diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Linear Technology LTC1665/LTC1660, 8 channels DAC + * + * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com> + */ +#include <linux/bitops.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#define LTC1660_REG_WAKE 0x0 +#define LTC1660_REG_DAC_A 0x1 +#define LTC1660_REG_DAC_B 0x2 +#define LTC1660_REG_DAC_C 0x3 +#define LTC1660_REG_DAC_D 0x4 +#define LTC1660_REG_DAC_E 0x5 +#define LTC1660_REG_DAC_F 0x6 +#define LTC1660_REG_DAC_G 0x7 +#define LTC1660_REG_DAC_H 0x8 +#define LTC1660_REG_SLEEP 0xe + +#define LTC1660_NUM_CHANNELS 8 + +static const struct regmap_config ltc1660_regmap_config = { + .reg_bits = 4, + .val_bits = 12, +}; + +enum ltc1660_supported_device_ids { + ID_LTC1660, + ID_LTC1665, +}; + +struct ltc1660_priv { + struct spi_device *spi; + struct regmap *regmap; + struct regulator *vref_reg; + unsigned int value[LTC1660_NUM_CHANNELS]; + unsigned int vref_mv; +}; + +static int ltc1660_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ltc1660_priv *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = priv->value[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = regulator_get_voltage(priv->vref_reg); + if (*val < 0) { + dev_err(&priv->spi->dev, "failed to read vref regulator: %d\n", + *val); + return *val; + } + + /* Convert to mV */ + *val /= 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ltc1660_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ltc1660_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val2 != 0) + return -EINVAL; + + if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) + return -EINVAL; + + ret = regmap_write(priv->regmap, chan->channel, + (val << chan->scan_type.shift)); + if (!ret) + priv->value[chan->channel] = val; + + return ret; + default: + return -EINVAL; + } +} + +#define LTC1660_CHAN(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + }, \ +} + +#define LTC1660_OCTAL_CHANNELS(bits) { \ + LTC1660_CHAN(LTC1660_REG_DAC_A, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_B, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_C, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_D, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_E, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_F, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_G, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_H, bits), \ +} + +static const struct iio_chan_spec ltc1660_channels[][LTC1660_NUM_CHANNELS] = { + [ID_LTC1660] = LTC1660_OCTAL_CHANNELS(10), + [ID_LTC1665] = LTC1660_OCTAL_CHANNELS(8), +}; + +static const struct iio_info ltc1660_info = { + .read_raw = &ltc1660_read_raw, + .write_raw = &ltc1660_write_raw, +}; + +static int __maybe_unused ltc1660_suspend(struct device *dev) +{ + struct ltc1660_priv *priv = iio_priv(spi_get_drvdata( + to_spi_device(dev))); + return regmap_write(priv->regmap, LTC1660_REG_SLEEP, 0x00); +} + +static int __maybe_unused ltc1660_resume(struct device *dev) +{ + struct ltc1660_priv *priv = iio_priv(spi_get_drvdata( + to_spi_device(dev))); + return regmap_write(priv->regmap, LTC1660_REG_WAKE, 0x00); +} +static SIMPLE_DEV_PM_OPS(ltc1660_pm_ops, ltc1660_suspend, ltc1660_resume); + +static int ltc1660_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ltc1660_priv *priv; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); + if (indio_dev == NULL) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->regmap = devm_regmap_init_spi(spi, &ltc1660_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&spi->dev, "failed to register spi regmap %ld\n", + PTR_ERR(priv->regmap)); + return PTR_ERR(priv->regmap); + } + + priv->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(priv->vref_reg)) { + dev_err(&spi->dev, "vref regulator not specified\n"); + return PTR_ERR(priv->vref_reg); + } + + ret = regulator_enable(priv->vref_reg); + if (ret) { + dev_err(&spi->dev, "failed to enable vref regulator: %d\n", + ret); + return ret; + } + + priv->spi = spi; + spi_set_drvdata(spi, indio_dev); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ltc1660_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc1660_channels[id->driver_data]; + indio_dev->num_channels = LTC1660_NUM_CHANNELS; + indio_dev->name = id->name; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "failed to register iio device: %d\n", + ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + regulator_disable(priv->vref_reg); + + return ret; +} + +static int ltc1660_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ltc1660_priv *priv = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(priv->vref_reg); + + return 0; +} + +static const struct of_device_id ltc1660_dt_ids[] = { + { .compatible = "lltc,ltc1660", .data = (void *)ID_LTC1660 }, + { .compatible = "lltc,ltc1665", .data = (void *)ID_LTC1665 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ltc1660_dt_ids); + +static const struct spi_device_id ltc1660_id[] = { + {"ltc1660", ID_LTC1660}, + {"ltc1665", ID_LTC1665}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, ltc1660_id); + +static struct spi_driver ltc1660_driver = { + .driver = { + .name = "ltc1660", + .of_match_table = ltc1660_dt_ids, + .pm = &ltc1660_pm_ops, + }, + .probe = ltc1660_probe, + .remove = ltc1660_remove, + .id_table = ltc1660_id, +}; +module_spi_driver(ltc1660_driver); + +MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); +MODULE_DESCRIPTION("Linear Technology LTC1660/LTC1665 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c @@ -113,15 +113,14 @@ static int max517_write_raw(struct iio_dev *indio_dev, return ret; } -#ifdef CONFIG_PM_SLEEP -static int max517_suspend(struct device *dev) +static int __maybe_unused max517_suspend(struct device *dev) { u8 outbuf = COMMAND_PD; return i2c_master_send(to_i2c_client(dev), &outbuf, 1); } -static int max517_resume(struct device *dev) +static int __maybe_unused max517_resume(struct device *dev) { u8 outbuf = 0; @@ -129,10 +128,6 @@ static int max517_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); -#define MAX517_PM_OPS (&max517_pm_ops) -#else -#define MAX517_PM_OPS NULL -#endif static const struct iio_info max517_info = { .read_raw = max517_read_raw, @@ -229,7 +224,7 @@ MODULE_DEVICE_TABLE(i2c, max517_id); static struct i2c_driver max517_driver = { .driver = { .name = MAX517_DRV_NAME, - .pm = MAX517_PM_OPS, + .pm = &max517_pm_ops, }, .probe = max517_probe, .remove = max517_remove, diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c @@ -270,8 +270,7 @@ static int max5821_write_raw(struct iio_dev *indio_dev, } } -#ifdef CONFIG_PM_SLEEP -static int max5821_suspend(struct device *dev) +static int __maybe_unused max5821_suspend(struct device *dev) { u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE, MAX5821_EXTENDED_DAC_A | @@ -281,7 +280,7 @@ static int max5821_suspend(struct device *dev) return i2c_master_send(to_i2c_client(dev), outbuf, 2); } -static int max5821_resume(struct device *dev) +static int __maybe_unused max5821_resume(struct device *dev) { u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE, MAX5821_EXTENDED_DAC_A | @@ -292,10 +291,6 @@ static int max5821_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume); -#define MAX5821_PM_OPS (&max5821_pm_ops) -#else -#define MAX5821_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ static const struct iio_info max5821_info = { .read_raw = max5821_read_raw, @@ -392,7 +387,7 @@ static struct i2c_driver max5821_driver = { .driver = { .name = "max5821", .of_match_table = max5821_of_match, - .pm = MAX5821_PM_OPS, + .pm = &max5821_pm_ops, }, .probe = max5821_probe, .remove = max5821_remove, diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c @@ -45,7 +45,7 @@ struct mcp4725_data { struct regulator *vref_reg; }; -static int mcp4725_suspend(struct device *dev) +static int __maybe_unused mcp4725_suspend(struct device *dev) { struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); @@ -58,7 +58,7 @@ static int mcp4725_suspend(struct device *dev) return i2c_master_send(data->client, outbuf, 2); } -static int mcp4725_resume(struct device *dev) +static int __maybe_unused mcp4725_resume(struct device *dev) { struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); @@ -71,13 +71,7 @@ static int mcp4725_resume(struct device *dev) return i2c_master_send(data->client, outbuf, 2); } - -#ifdef CONFIG_PM_SLEEP static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); -#define MCP4725_PM_OPS (&mcp4725_pm_ops) -#else -#define MCP4725_PM_OPS NULL -#endif static ssize_t mcp4725_store_eeprom(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -547,7 +541,7 @@ static struct i2c_driver mcp4725_driver = { .driver = { .name = MCP4725_DRV_NAME, .of_match_table = of_match_ptr(mcp4725_of_match), - .pm = MCP4725_PM_OPS, + .pm = &mcp4725_pm_ops, }, .probe = mcp4725_probe, .remove = mcp4725_remove, diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c @@ -94,17 +94,22 @@ static int mcp4922_write_raw(struct iio_dev *indio_dev, long mask) { struct mcp4922_state *state = iio_priv(indio_dev); + int ret; if (val2 != 0) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > GENMASK(chan->scan_type.realbits-1, 0)) + if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) return -EINVAL; val <<= chan->scan_type.shift; - state->value[chan->channel] = val; - return mcp4922_spi_write(state, chan->channel, val); + + ret = mcp4922_spi_write(state, chan->channel, val); + if (!ret) + state->value[chan->channel] = val; + return ret; + default: return -EINVAL; } diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c @@ -421,6 +421,7 @@ MODULE_DEVICE_TABLE(i2c, dac5571_id); static struct i2c_driver dac5571_driver = { .driver = { .name = "ti-dac5571", + .of_match_table = of_match_ptr(dac5571_of_id), }, .probe = dac5571_probe, .remove = dac5571_remove, diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c @@ -1078,6 +1078,6 @@ static struct spi_driver ad9523_driver = { }; module_spi_driver(ad9523_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c @@ -388,7 +388,7 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) if (!pdata) return NULL; - strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1); + snprintf(&pdata->name[0], SPI_NAME_SIZE - 1, "%pOFn", np); tmp = 10000; of_property_read_u32(np, "adi,channel-spacing", &tmp); diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c @@ -282,9 +282,11 @@ static int max30102_read_measurement(struct max30102_data *data, switch (measurements) { case 3: MAX30102_COPY_DATA(2); - case 2: /* fall-through */ + /* fall through */ + case 2: MAX30102_COPY_DATA(1); - case 1: /* fall-through */ + /* fall through */ + case 1: MAX30102_COPY_DATA(0); break; default: diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -23,6 +23,7 @@ #include <linux/iio/iio.h> #include <linux/acpi.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include "inv_mpu_iio.h" /* @@ -926,6 +927,39 @@ error_power_off: return result; } +static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st) +{ + int result; + + result = regulator_enable(st->vddio_supply); + if (result) { + dev_err(regmap_get_device(st->map), + "Failed to enable regulator: %d\n", result); + } else { + /* Give the device a little bit of time to start up. */ + usleep_range(35000, 70000); + } + + return result; +} + +static int inv_mpu_core_disable_regulator(struct inv_mpu6050_state *st) +{ + int result; + + result = regulator_disable(st->vddio_supply); + if (result) + dev_err(regmap_get_device(st->map), + "Failed to disable regulator: %d\n", result); + + return result; +} + +static void inv_mpu_core_disable_regulator_action(void *_data) +{ + inv_mpu_core_disable_regulator(_data); +} + int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type) { @@ -992,6 +1026,28 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return -EINVAL; } + st->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) { + if (PTR_ERR(st->vddio_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vddio regulator %d\n", + (int)PTR_ERR(st->vddio_supply)); + + return PTR_ERR(st->vddio_supply); + } + + result = inv_mpu_core_enable_regulator(st); + if (result) + return result; + + result = devm_add_action(dev, inv_mpu_core_disable_regulator_action, + st); + if (result) { + inv_mpu_core_disable_regulator_action(st); + dev_err(dev, "Failed to setup regulator cleanup action %d\n", + result); + return result; + } + /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st); if (result) @@ -1051,7 +1107,12 @@ static int inv_mpu_resume(struct device *dev) int result; mutex_lock(&st->lock); + result = inv_mpu_core_enable_regulator(st); + if (result) + goto out_unlock; + result = inv_mpu6050_set_power_itg(st, true); +out_unlock: mutex_unlock(&st->lock); return result; @@ -1064,6 +1125,7 @@ static int inv_mpu_suspend(struct device *dev) mutex_lock(&st->lock); result = inv_mpu6050_set_power_itg(st, false); + inv_mpu_core_disable_regulator(st); mutex_unlock(&st->lock); return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -129,6 +129,7 @@ struct inv_mpu6050_hw { * @chip_period: chip internal period estimation (~1kHz). * @it_timestamp: timestamp from previous interrupt. * @data_timestamp: timestamp for next data sample. + * @vddio_supply voltage regulator for the chip. */ struct inv_mpu6050_state { struct mutex lock; @@ -149,6 +150,7 @@ struct inv_mpu6050_state { s64 chip_period; s64 it_timestamp; s64 data_timestamp; + struct regulator *vddio_supply; }; /*register and associated bit definition*/ diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -9,7 +9,7 @@ config IIO_ST_LSM6DSX help Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, - ism330dlc + ism330dlc, lsm6dso To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -19,6 +19,7 @@ #define ST_LSM6DSL_DEV_NAME "lsm6dsl" #define ST_LSM6DSM_DEV_NAME "lsm6dsm" #define ST_ISM330DLC_DEV_NAME "ism330dlc" +#define ST_LSM6DSO_DEV_NAME "lsm6dso" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -26,14 +27,20 @@ enum st_lsm6dsx_hw_id { ST_LSM6DSL_ID, ST_LSM6DSM_ID, ST_ISM330DLC_ID, + ST_LSM6DSO_ID, ST_LSM6DSX_MAX_ID, }; -#define ST_LSM6DSX_BUFF_SIZE 400 +#define ST_LSM6DSX_BUFF_SIZE 512 #define ST_LSM6DSX_CHAN_SIZE 2 #define ST_LSM6DSX_SAMPLE_SIZE 6 +#define ST_LSM6DSX_TAG_SIZE 1 +#define ST_LSM6DSX_TAGGED_SAMPLE_SIZE (ST_LSM6DSX_SAMPLE_SIZE + \ + ST_LSM6DSX_TAG_SIZE) #define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \ ST_LSM6DSX_SAMPLE_SIZE) +#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \ + * ST_LSM6DSX_TAGGED_SAMPLE_SIZE) #define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) struct st_lsm6dsx_reg { @@ -41,13 +48,17 @@ struct st_lsm6dsx_reg { u8 mask; }; +struct st_lsm6dsx_hw; + /** * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings + * @read_fifo: Read FIFO callback. * @fifo_th: FIFO threshold register info (addr + mask). * @fifo_diff: FIFO diff status register info (addr + mask). * @th_wl: FIFO threshold word length. */ struct st_lsm6dsx_fifo_ops { + int (*read_fifo)(struct st_lsm6dsx_hw *hw); struct { u8 addr; u16 mask; @@ -79,6 +90,7 @@ struct st_lsm6dsx_hw_ts_settings { * @max_fifo_size: Sensor max fifo length in FIFO words. * @id: List of hw id supported by the driver configuration. * @decimator: List of decimator register info (addr + mask). + * @batch: List of FIFO batching register info (addr + mask). * @fifo_ops: Sensor hw FIFO parameters. * @ts_settings: Hw timer related settings. */ @@ -87,6 +99,7 @@ struct st_lsm6dsx_settings { u16 max_fifo_size; enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; + struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_fifo_ops fifo_ops; struct st_lsm6dsx_hw_ts_settings ts_settings; }; @@ -175,5 +188,8 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_fifo_mode fifo_mode); +int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val); #endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -12,6 +12,11 @@ * buffer contains the data of all the enabled FIFO data sets * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the * value of the decimation factor and ODR set for each FIFO data set. + * + * LSM6DSO: The FIFO buffer can be configured to store data from gyroscope and + * accelerometer. Each sample is queued with a tag (1B) indicating data source + * (gyroscope, accelerometer, hw timer). + * * FIFO supported modes: * - BYPASS: FIFO disabled * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index @@ -46,6 +51,7 @@ #define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) #define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) #define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e +#define ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR 0x78 #define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42 #define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 @@ -58,6 +64,12 @@ struct st_lsm6dsx_decimator_entry { u8 val; }; +enum st_lsm6dsx_fifo_tag { + ST_LSM6DSX_GYRO_TAG = 0x01, + ST_LSM6DSX_ACC_TAG = 0x02, + ST_LSM6DSX_TS_TAG = 0x04, +}; + static const struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { { 0, 0x0 }, @@ -177,12 +189,34 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *batch_reg; u8 data; - data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; - return regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_ODR_MASK, - FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, data)); + batch_reg = &hw->settings->batch[sensor->id]; + if (batch_reg->addr) { + int val; + + if (enable) { + int err; + + err = st_lsm6dsx_check_odr(sensor, sensor->odr, + &data); + if (err < 0) + return err; + } else { + data = 0; + } + val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask); + return regmap_update_bits(hw->regmap, batch_reg->addr, + batch_reg->mask, val); + } else { + data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; + return regmap_update_bits(hw->regmap, + ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_ODR_MASK, + FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, + data)); + } } int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) @@ -250,21 +284,21 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) } /* - * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid - * a kmalloc for each bus access + * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN + * in order to avoid a kmalloc for each bus access */ -static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, - unsigned int data_len) +static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, + u8 *data, unsigned int data_len, + unsigned int max_word_len) { unsigned int word_len, read_len = 0; int err; while (read_len < data_len) { word_len = min_t(unsigned int, data_len - read_len, - ST_LSM6DSX_MAX_WORD_LEN); - err = regmap_bulk_read(hw->regmap, - ST_LSM6DSX_REG_FIFO_OUTL_ADDR, - data + read_len, word_len); + max_word_len); + err = regmap_bulk_read(hw->regmap, addr, data + read_len, + word_len); if (err < 0) return err; read_len += word_len; @@ -282,7 +316,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, * * Return: Number of bytes read from the FIFO */ -static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) +int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) { u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; @@ -314,7 +348,9 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { - err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len); + err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, + hw->buff, pattern_len, + ST_LSM6DSX_MAX_WORD_LEN); if (err < 0) { dev_err(hw->dev, "failed to read pattern from fifo (err=%d)\n", @@ -400,13 +436,111 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) return read_len; } +/** + * st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * + * Read samples from the hw FIFO and push them to IIO buffers. + * + * Return: Number of bytes read from the FIFO + */ +int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) +{ + u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + u16 fifo_len, fifo_diff_mask; + struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor; + u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE], tag; + bool reset_ts = false; + int i, err, read_len; + __le16 fifo_status; + s64 ts = 0; + + err = regmap_bulk_read(hw->regmap, + hw->settings->fifo_ops.fifo_diff.addr, + &fifo_status, sizeof(fifo_status)); + if (err < 0) { + dev_err(hw->dev, "failed to read fifo status (err=%d)\n", + err); + return err; + } + + fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; + fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * + ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + if (!fifo_len) + return 0; + + acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); + + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { + err = st_lsm6dsx_read_block(hw, + ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR, + hw->buff, pattern_len, + ST_LSM6DSX_MAX_TAGGED_WORD_LEN); + if (err < 0) { + dev_err(hw->dev, + "failed to read pattern from fifo (err=%d)\n", + err); + return err; + } + + for (i = 0; i < pattern_len; + i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) { + memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE], + ST_LSM6DSX_SAMPLE_SIZE); + + tag = hw->buff[i] >> 3; + switch (tag) { + case ST_LSM6DSX_TS_TAG: + /* + * hw timestamp is 4B long and it is stored + * in FIFO according to this schema: + * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16], + * B3 = ts[31:24] + */ + ts = le32_to_cpu(*((__le32 *)iio_buff)); + /* + * check if hw timestamp engine is going to + * reset (the sensor generates an interrupt + * to signal the hw timestamp will reset in + * 1.638s) + */ + if (!reset_ts && ts >= 0xffff0000) + reset_ts = true; + ts *= ST_LSM6DSX_TS_SENSITIVITY; + break; + case ST_LSM6DSX_GYRO_TAG: + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + iio_buff, gyro_sensor->ts_ref + ts); + break; + case ST_LSM6DSX_ACC_TAG: + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + iio_buff, acc_sensor->ts_ref + ts); + break; + default: + break; + } + } + } + + if (unlikely(reset_ts)) { + err = st_lsm6dsx_reset_hw_ts(hw); + if (err < 0) + return err; + } + return read_len; +} + int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) { int err; mutex_lock(&hw->fifo_lock); - st_lsm6dsx_read_fifo(hw); + hw->settings->fifo_ops.read_fifo(hw); err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS); mutex_unlock(&hw->fifo_lock); @@ -478,7 +612,7 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) int count; mutex_lock(&hw->fifo_lock); - count = st_lsm6dsx_read_fifo(hw); + count = hw->settings->fifo_ops.read_fifo(hw); mutex_unlock(&hw->fifo_lock); return !count ? IRQ_NONE : IRQ_HANDLED; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -23,6 +23,12 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 4KB * + * - LSM6DSO + * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 3KB + * * Copyright 2016 STMicroelectronics Inc. * * Lorenzo Bianconi <lorenzo.bianconi@st.com> @@ -171,6 +177,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, .fifo_ops = { + .read_fifo = st_lsm6dsx_read_fifo, .fifo_th = { .addr = 0x06, .mask = GENMASK(11, 0), @@ -217,6 +224,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, .fifo_ops = { + .read_fifo = st_lsm6dsx_read_fifo, .fifo_th = { .addr = 0x06, .mask = GENMASK(11, 0), @@ -265,6 +273,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, .fifo_ops = { + .read_fifo = st_lsm6dsx_read_fifo, .fifo_th = { .addr = 0x06, .mask = GENMASK(10, 0), @@ -294,6 +303,45 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, }, + { + .wai = 0x6c, + .max_fifo_size = 512, + .id = { + [0] = ST_LSM6DSO_ID, + }, + .batch = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x09, + .mask = GENMASK(3, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x09, + .mask = GENMASK(7, 4), + }, + }, + .fifo_ops = { + .read_fifo = st_lsm6dsx_read_tagged_fifo, + .fifo_th = { + .addr = 0x07, + .mask = GENMASK(8, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(8, 0), + }, + .th_wl = 1, + }, + .ts_settings = { + .timer_en = { + .addr = 0x19, + .mask = BIT(5), + }, + .decimator = { + .addr = 0x0a, + .mask = GENMASK(7, 6), + }, + }, + }, }; #define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ @@ -395,8 +443,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, return 0; } -static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, - u8 *val) +int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val) { int i; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -61,6 +61,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,ism330dlc", .data = (void *)ST_ISM330DLC_ID, }, + { + .compatible = "st,lsm6dso", + .data = (void *)ST_LSM6DSO_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -71,6 +75,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, + { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -61,6 +61,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,ism330dlc", .data = (void *)ST_ISM330DLC_ID, }, + { + .compatible = "st,lsm6dso", + .data = (void *)ST_LSM6DSO_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -71,6 +75,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, + { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Data sheets: * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf @@ -281,8 +278,7 @@ static int bh1750_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int bh1750_suspend(struct device *dev) +static int __maybe_unused bh1750_suspend(struct device *dev) { int ret; struct bh1750_data *data = @@ -300,10 +296,6 @@ static int bh1750_suspend(struct device *dev) } static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL); -#define BH1750_PM_OPS (&bh1750_pm_ops) -#else -#define BH1750_PM_OPS NULL -#endif static const struct i2c_device_id bh1750_id[] = { { "bh1710", BH1710 }, @@ -315,10 +307,21 @@ static const struct i2c_device_id bh1750_id[] = { }; MODULE_DEVICE_TABLE(i2c, bh1750_id); +static const struct of_device_id bh1750_of_match[] = { + { .compatible = "rohm,bh1710", }, + { .compatible = "rohm,bh1715", }, + { .compatible = "rohm,bh1721", }, + { .compatible = "rohm,bh1750", }, + { .compatible = "rohm,bh1751", }, + { } +}; +MODULE_DEVICE_TABLE(of, bh1750_of_match); + static struct i2c_driver bh1750_driver = { .driver = { .name = "bh1750", - .pm = BH1750_PM_OPS, + .of_match_table = bh1750_of_match, + .pm = &bh1750_pm_ops, }, .probe = bh1750_probe, .remove = bh1750_remove, diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c @@ -99,7 +99,6 @@ static const int max44000_alspga_shift[] = {0, 2, 4, 7}; * Handling this internally is also required for buffer support because the * channel's scan_type can't be modified dynamically. */ -static const int max44000_alstim_shift[] = {0, 2, 4, 6}; #define MAX44000_ALSTIM_SHIFT(alstim) (2 * (alstim)) /* Available integration times with pretty manual alignment: */ diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c @@ -20,6 +20,7 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/platform_data/tsl2772.h> +#include <linux/regulator/consumer.h> /* Cal defs */ #define PROX_STAT_CAL 0 @@ -107,6 +108,11 @@ #define TSL2772_ALS_GAIN_TRIM_MIN 250 #define TSL2772_ALS_GAIN_TRIM_MAX 4000 +#define TSL2772_MAX_PROX_LEDS 2 + +#define TSL2772_BOOT_MIN_SLEEP_TIME 10000 +#define TSL2772_BOOT_MAX_SLEEP_TIME 28000 + /* Device family members */ enum { tsl2571, @@ -118,7 +124,8 @@ enum { tsl2672, tmd2672, tsl2772, - tmd2772 + tmd2772, + apds9930, }; enum { @@ -141,11 +148,21 @@ struct tsl2772_chip_info { const struct iio_info *info; }; +static const int tsl2772_led_currents[][2] = { + { 100000, TSL2772_100_mA }, + { 50000, TSL2772_50_mA }, + { 25000, TSL2772_25_mA }, + { 13000, TSL2772_13_mA }, + { 0, 0 } +}; + struct tsl2772_chip { kernel_ulong_t id; struct mutex prox_mutex; struct mutex als_mutex; struct i2c_client *client; + struct regulator *vdd_supply; + struct regulator *vddio_supply; u16 prox_data; struct tsl2772_als_info als_cur_info; struct tsl2772_settings settings; @@ -197,6 +214,12 @@ static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { { 0, 0 }, }; +static const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 52000, 96824 }, + { 38792, 67132 }, + { 0, 0 }, +}; + static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { [tsl2571] = tsl2x71_lux_table, [tsl2671] = tsl2x71_lux_table, @@ -208,6 +231,7 @@ static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { [tmd2672] = tmd2x72_lux_table, [tsl2772] = tsl2x72_lux_table, [tmd2772] = tmd2x72_lux_table, + [apds9930] = apds9930_lux_table, }; static const struct tsl2772_settings tsl2772_default_settings = { @@ -258,6 +282,7 @@ static const int tsl2772_int_time_avail[][6] = { [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, + [apds9930] = { 0, 2730, 0, 2730, 0, 699000 }, }; static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; @@ -283,7 +308,8 @@ static const u8 device_channel_config[] = { [tsl2672] = PRX2, [tmd2672] = PRX2, [tsl2772] = ALSPRX2, - [tmd2772] = ALSPRX2 + [tmd2772] = ALSPRX2, + [apds9930] = ALSPRX2, }; static int tsl2772_read_status(struct tsl2772_chip *chip) @@ -497,6 +523,7 @@ static int tsl2772_get_prox(struct iio_dev *indio_dev) case tmd2672: case tsl2772: case tmd2772: + case apds9930: if (!(ret & TSL2772_STA_PRX_VALID)) { ret = -EINVAL; goto prox_poll_err; @@ -515,6 +542,75 @@ prox_poll_err: return ret; } +static int tsl2772_read_prox_led_current(struct tsl2772_chip *chip) +{ + struct device_node *of_node = chip->client->dev.of_node; + int ret, tmp, i; + + ret = of_property_read_u32(of_node, "led-max-microamp", &tmp); + if (ret < 0) + return ret; + + for (i = 0; tsl2772_led_currents[i][0] != 0; i++) { + if (tmp == tsl2772_led_currents[i][0]) { + chip->settings.prox_power = tsl2772_led_currents[i][1]; + return 0; + } + } + + dev_err(&chip->client->dev, "Invalid value %d for led-max-microamp\n", + tmp); + + return -EINVAL; + +} + +static int tsl2772_read_prox_diodes(struct tsl2772_chip *chip) +{ + struct device_node *of_node = chip->client->dev.of_node; + int i, ret, num_leds, prox_diode_mask; + u32 leds[TSL2772_MAX_PROX_LEDS]; + + ret = of_property_count_u32_elems(of_node, "amstaos,proximity-diodes"); + if (ret < 0) + return ret; + + num_leds = ret; + if (num_leds > TSL2772_MAX_PROX_LEDS) + num_leds = TSL2772_MAX_PROX_LEDS; + + ret = of_property_read_u32_array(of_node, "amstaos,proximity-diodes", + leds, num_leds); + if (ret < 0) { + dev_err(&chip->client->dev, + "Invalid value for amstaos,proximity-diodes: %d.\n", + ret); + return ret; + } + + prox_diode_mask = 0; + for (i = 0; i < num_leds; i++) { + if (leds[i] == 0) + prox_diode_mask |= TSL2772_DIODE0; + else if (leds[i] == 1) + prox_diode_mask |= TSL2772_DIODE1; + else { + dev_err(&chip->client->dev, + "Invalid value %d in amstaos,proximity-diodes.\n", + leds[i]); + return -EINVAL; + } + } + + return 0; +} + +static void tsl2772_parse_dt(struct tsl2772_chip *chip) +{ + tsl2772_read_prox_led_current(chip); + tsl2772_read_prox_diodes(chip); +} + /** * tsl2772_defaults() - Populates the device nominal operating parameters * with those provided by a 'platform' data struct or @@ -541,6 +637,8 @@ static void tsl2772_defaults(struct tsl2772_chip *chip) memcpy(chip->tsl2772_device_lux, tsl2772_default_lux_table_group[chip->id], TSL2772_DEFAULT_TABLE_BYTES); + + tsl2772_parse_dt(chip); } /** @@ -595,6 +693,52 @@ static int tsl2772_als_calibrate(struct iio_dev *indio_dev) return ret; } +static void tsl2772_disable_regulators_action(void *_data) +{ + struct tsl2772_chip *chip = _data; + + regulator_disable(chip->vdd_supply); + regulator_disable(chip->vddio_supply); +} + +static int tsl2772_enable_regulator(struct tsl2772_chip *chip, + struct regulator *regulator) +{ + int ret; + + ret = regulator_enable(regulator); + if (ret < 0) { + dev_err(&chip->client->dev, "Failed to enable regulator: %d\n", + ret); + return ret; + } + + return 0; +} + +static struct regulator *tsl2772_get_regulator(struct tsl2772_chip *chip, + char *name) +{ + struct regulator *regulator; + int ret; + + regulator = devm_regulator_get(&chip->client->dev, name); + if (IS_ERR(regulator)) { + if (PTR_ERR(regulator) != -EPROBE_DEFER) + dev_err(&chip->client->dev, + "Failed to get %s regulator %d\n", + name, (int)PTR_ERR(regulator)); + + return regulator; + } + + ret = tsl2772_enable_regulator(chip, regulator); + if (ret < 0) + return ERR_PTR(ret); + + return regulator; +} + static int tsl2772_chip_on(struct iio_dev *indio_dev) { struct tsl2772_chip *chip = iio_priv(indio_dev); @@ -1260,6 +1404,7 @@ static int tsl2772_device_id_verif(int id, int target) case tmd2672: case tsl2772: case tmd2772: + case apds9930: return (id & 0xf0) == SWORDFISH_ID; } @@ -1652,6 +1797,27 @@ static int tsl2772_probe(struct i2c_client *clientp, chip->client = clientp; i2c_set_clientdata(clientp, indio_dev); + chip->vddio_supply = tsl2772_get_regulator(chip, "vddio"); + if (IS_ERR(chip->vddio_supply)) + return PTR_ERR(chip->vddio_supply); + + chip->vdd_supply = tsl2772_get_regulator(chip, "vdd"); + if (IS_ERR(chip->vdd_supply)) { + regulator_disable(chip->vddio_supply); + return PTR_ERR(chip->vdd_supply); + } + + ret = devm_add_action(&clientp->dev, tsl2772_disable_regulators_action, + chip); + if (ret < 0) { + tsl2772_disable_regulators_action(chip); + dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n", + ret); + return ret; + } + + usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); + ret = i2c_smbus_read_byte_data(chip->client, TSL2772_CMD_REG | TSL2772_CHIPID); if (ret < 0) @@ -1725,13 +1891,33 @@ static int tsl2772_probe(struct i2c_client *clientp, static int tsl2772_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret; + + ret = tsl2772_chip_off(indio_dev); + regulator_disable(chip->vdd_supply); + regulator_disable(chip->vddio_supply); - return tsl2772_chip_off(indio_dev); + return ret; } static int tsl2772_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret; + + ret = tsl2772_enable_regulator(chip, chip->vddio_supply); + if (ret < 0) + return ret; + + ret = tsl2772_enable_regulator(chip, chip->vdd_supply); + if (ret < 0) { + regulator_disable(chip->vddio_supply); + return ret; + } + + usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); return tsl2772_chip_on(indio_dev); } @@ -1758,6 +1944,7 @@ static const struct i2c_device_id tsl2772_idtable[] = { { "tmd2672", tmd2672 }, { "tsl2772", tsl2772 }, { "tmd2772", tmd2772 }, + { "apds9930", apds9930}, {} }; @@ -1774,6 +1961,7 @@ static const struct of_device_id tsl2772_of_match[] = { { .compatible = "amstaos,tmd2672" }, { .compatible = "amstaos,tsl2772" }, { .compatible = "amstaos,tmd2772" }, + { .compatible = "avago,apds9930" }, {} }; MODULE_DEVICE_TABLE(of, tsl2772_of_match); diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h @@ -31,7 +31,7 @@ enum hmc5843_ids { }; /** - * struct hcm5843_data - device specific data + * struct hmc5843_data - device specific data * @dev: actual device * @lock: update and read regmap data * @regmap: hardware access register maps diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * IIO multiplexer driver * * Copyright (C) 2017 Axentia Technologies AB * * Author: Peter Rosin <peda@axentia.se> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/err.h> diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c @@ -137,7 +137,6 @@ static int max5481_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct max5481_data *data; const struct spi_device_id *id = spi_get_device_id(spi); - const struct of_device_id *match; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); @@ -149,10 +148,8 @@ static int max5481_probe(struct spi_device *spi) data->spi = spi; - match = of_match_device(of_match_ptr(max5481_match), &spi->dev); - if (match) - data->cfg = of_device_get_match_data(&spi->dev); - else + data->cfg = of_device_get_match_data(&spi->dev); + if (!data->cfg) data->cfg = &max5481_cfg[id->driver_data]; indio_dev->name = id->name; diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c @@ -147,7 +147,6 @@ static int mcp4018_probe(struct i2c_client *client) struct device *dev = &client->dev; struct mcp4018_data *data; struct iio_dev *indio_dev; - const struct of_device_id *match; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { @@ -162,10 +161,8 @@ static int mcp4018_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - match = of_match_device(of_match_ptr(mcp4018_of_match), dev); - if (match) - data->cfg = of_device_get_match_data(dev); - else + data->cfg = of_device_get_match_data(dev); + if (!data->cfg) data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data]; indio_dev->dev.parent = dev; @@ -190,4 +187,4 @@ module_i2c_driver(mcp4018_driver); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_DESCRIPTION("MCP4018 digital potentiometer"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Industrial I/O driver for Microchip digital potentiometers * Copyright (c) 2015 Axentia Technologies AB @@ -22,10 +23,6 @@ * mcp4652 2 257 5, 10, 50, 100 01011xx * mcp4661 2 257 5, 10, 50, 100 0101xxx * mcp4662 2 257 5, 10, 50, 100 01011xx - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #include <linux/module.h> @@ -360,7 +357,6 @@ static int mcp4531_probe(struct i2c_client *client) struct device *dev = &client->dev; struct mcp4531_data *data; struct iio_dev *indio_dev; - const struct of_device_id *match; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { @@ -375,10 +371,8 @@ static int mcp4531_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - match = of_match_device(of_match_ptr(mcp4531_of_match), dev); - if (match) - data->cfg = of_device_get_match_data(dev); - else + data->cfg = of_device_get_match_data(dev); + if (!data->cfg) data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data]; indio_dev->dev.parent = dev; @@ -403,4 +397,4 @@ module_i2c_driver(mcp4531_driver); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_DESCRIPTION("MCP4531 digital potentiometer"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * MS5611 pressure and temperature sensor driver * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #ifndef _MS5611_H diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MS5611 pressure and temperature sensor driver * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Data sheet: * http://www.meas-spec.com/downloads/MS5611-01BA03.pdf * http://www.meas-spec.com/downloads/MS5607-02BA03.pdf diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MS5611 pressure and temperature sensor driver (I2C bus) * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * 7-bit I2C slave addresses: * * 0x77 (CSB pin low) @@ -117,9 +114,7 @@ static int ms5611_i2c_remove(struct i2c_client *client) #if defined(CONFIG_OF) static const struct of_device_id ms5611_i2c_matches[] = { { .compatible = "meas,ms5611" }, - { .compatible = "ms5611" }, { .compatible = "meas,ms5607" }, - { .compatible = "ms5607" }, { } }; MODULE_DEVICE_TABLE(of, ms5611_i2c_matches); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MS5611 pressure and temperature sensor driver (SPI bus) * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/delay.h> @@ -119,9 +116,7 @@ static int ms5611_spi_remove(struct spi_device *spi) #if defined(CONFIG_OF) static const struct of_device_id ms5611_spi_matches[] = { { .compatible = "meas,ms5611" }, - { .compatible = "ms5611" }, { .compatible = "meas,ms5607" }, - { .compatible = "ms5607" }, { } }; MODULE_DEVICE_TABLE(of, ms5611_spi_matches); diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig @@ -92,4 +92,15 @@ config SRF08 To compile this driver as a module, choose M here: the module will be called srf08. +config VL53L0X_I2C + tristate "STMicroelectronics VL53L0X ToF ranger sensor (I2C)" + depends on I2C + help + Say Y here to build a driver for STMicroelectronics VL53L0X + ToF ranger sensors with i2c interface. + This driver can be used to measure the distance of objects. + + To compile this driver as a module, choose M here: the + module will be called vl53l0x-i2c. + endmenu diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_RFD77402) += rfd77402.o obj-$(CONFIG_SRF04) += srf04.o obj-$(CONFIG_SRF08) += srf08.o obj-$(CONFIG_SX9500) += sx9500.o +obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o + diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c @@ -232,7 +232,6 @@ static u32 isl29501_register_write(struct isl29501_private *isl29501, u32 value) { const struct isl29501_register_desc *reg = &isl29501_registers[name]; - u8 msb, lsb; int ret; if (!reg->msb && value > U8_MAX) @@ -241,22 +240,15 @@ static u32 isl29501_register_write(struct isl29501_private *isl29501, if (value > U16_MAX) return -ERANGE; - if (!reg->msb) { - lsb = value & 0xFF; - } else { - msb = (value >> 8) & 0xFF; - lsb = value & 0xFF; - } - mutex_lock(&isl29501->lock); if (reg->msb) { ret = i2c_smbus_write_byte_data(isl29501->client, - reg->msb, msb); + reg->msb, value >> 8); if (ret < 0) goto err; } - ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb); + ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, value); err: mutex_unlock(&isl29501->lock); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for ST VL53L0X FlightSense ToF Ranging Sensor on a i2c bus. + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> + * + * Datasheet available at + * <https://www.st.com/resource/en/datasheet/vl53l0x.pdf> + * + * Default 7-bit i2c slave address 0x29. + * + * TODO: FIFO buffer, continuous mode, interrupts, range selection, + * sensor ID check. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +#define VL_REG_SYSRANGE_START 0x00 + +#define VL_REG_SYSRANGE_MODE_MASK GENMASK(3, 0) +#define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00 +#define VL_REG_SYSRANGE_MODE_START_STOP BIT(0) +#define VL_REG_SYSRANGE_MODE_BACKTOBACK BIT(1) +#define VL_REG_SYSRANGE_MODE_TIMED BIT(2) +#define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3) + +#define VL_REG_RESULT_INT_STATUS 0x13 +#define VL_REG_RESULT_RANGE_STATUS 0x14 +#define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0) + +struct vl53l0x_data { + struct i2c_client *client; +}; + +static int vl53l0x_read_proximity(struct vl53l0x_data *data, + const struct iio_chan_spec *chan, + int *val) +{ + struct i2c_client *client = data->client; + u16 tries = 20; + u8 buffer[12]; + int ret; + + ret = i2c_smbus_write_byte_data(client, VL_REG_SYSRANGE_START, 1); + if (ret < 0) + return ret; + + do { + ret = i2c_smbus_read_byte_data(client, + VL_REG_RESULT_RANGE_STATUS); + if (ret < 0) + return ret; + + if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE) + break; + + usleep_range(1000, 5000); + } while (--tries); + if (!tries) + return -ETIMEDOUT; + + ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS, + 12, buffer); + if (ret < 0) + return ret; + else if (ret != 12) + return -EREMOTEIO; + + /* Values should be between 30~1200 in millimeters. */ + *val = (buffer[10] << 8) + buffer[11]; + + return 0; +} + +static const struct iio_chan_spec vl53l0x_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int vl53l0x_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_DISTANCE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = vl53l0x_read_proximity(data, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info vl53l0x_info = { + .read_raw = vl53l0x_read_raw, +}; + +static int vl53l0x_probe(struct i2c_client *client) +{ + struct vl53l0x_data *data; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + i2c_set_clientdata(client, indio_dev); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK | + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EOPNOTSUPP; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = "vl53l0x"; + indio_dev->info = &vl53l0x_info; + indio_dev->channels = vl53l0x_channels; + indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id st_vl53l0x_dt_match[] = { + { .compatible = "st,vl53l0x", }, + { } +}; +MODULE_DEVICE_TABLE(of, st_vl53l0x_dt_match); + +static struct i2c_driver vl53l0x_driver = { + .driver = { + .name = "vl53l0x-i2c", + .of_match_table = st_vl53l0x_dt_match, + }, + .probe_new = vl53l0x_probe, +}; +module_i2c_driver(vl53l0x_driver); + +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); +MODULE_DESCRIPTION("ST vl53l0x ToF ranging sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c @@ -222,7 +222,7 @@ static void __exit iio_sysfs_trig_exit(void) } module_exit(iio_sysfs_trig_exit); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:iio-trig-sysfs"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig @@ -80,8 +80,6 @@ source "drivers/staging/netlogic/Kconfig" source "drivers/staging/mt29f_spinand/Kconfig" -source "drivers/staging/dgnc/Kconfig" - source "drivers/staging/gs_fpgaboot/Kconfig" source "drivers/staging/unisys/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/ obj-$(CONFIG_LTE_GDM724X) += gdm724x/ obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ obj-$(CONFIG_GOLDFISH) += goldfish/ -obj-$(CONFIG_DGNC) += dgnc/ obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ obj-$(CONFIG_UNISYSSPAR) += unisys/ diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h @@ -157,8 +157,6 @@ struct ion_heap_ops { * @lock: protects the free list * @waitqueue: queue to wait on from deferred free thread * @task: task struct of deferred free thread - * @debug_show: called when heap debug file is read to add any - * heap specific debug info to output * * Represents a pool of memory from which buffers can be made. In some * systems the only heap is regular system memory allocated via vmalloc. @@ -179,9 +177,6 @@ struct ion_heap { spinlock_t free_lock; wait_queue_head_t waitqueue; struct task_struct *task; - - int (*debug_show)(struct ion_heap *heap, struct seq_file *s, - void *unused); }; /** diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c @@ -212,29 +212,6 @@ static struct ion_heap_ops system_heap_ops = { .shrink = ion_system_heap_shrink, }; -static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s, - void *unused) -{ - struct ion_system_heap *sys_heap = container_of(heap, - struct ion_system_heap, - heap); - int i; - struct ion_page_pool *pool; - - for (i = 0; i < NUM_ORDERS; i++) { - pool = sys_heap->pools[i]; - - seq_printf(s, "%d order %u highmem pages %lu total\n", - pool->high_count, pool->order, - (PAGE_SIZE << pool->order) * pool->high_count); - seq_printf(s, "%d order %u lowmem pages %lu total\n", - pool->low_count, pool->order, - (PAGE_SIZE << pool->order) * pool->low_count); - } - - return 0; -} - static void ion_system_heap_destroy_pools(struct ion_page_pool **pools) { int i; @@ -281,7 +258,6 @@ static struct ion_heap *__ion_system_heap_create(void) if (ion_system_heap_create_pools(heap->pools)) goto free_heap; - heap->heap.debug_show = ion_system_heap_debug_show; return &heap->heap; free_heap: diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c @@ -27,8 +27,6 @@ #include <linux/interrupt.h> #include <linux/param.h> #include <linux/fs.h> -#include <linux/device.h> -#include <linux/cdev.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/jiffies.h> @@ -364,11 +362,11 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf, * if nothing is currently available */ spin_lock_irq(&fifo->read_queue_lock); - ret = wait_event_interruptible_lock_irq_timeout( - fifo->read_queue, - ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), - fifo->read_queue_lock, - (read_timeout >= 0) ? msecs_to_jiffies(read_timeout) : + ret = wait_event_interruptible_lock_irq_timeout + (fifo->read_queue, + ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), + fifo->read_queue_lock, + (read_timeout >= 0) ? msecs_to_jiffies(read_timeout) : MAX_SCHEDULE_TIMEOUT); spin_unlock_irq(&fifo->read_queue_lock); @@ -482,12 +480,12 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, * currently enough room in the fifo */ spin_lock_irq(&fifo->write_queue_lock); - ret = wait_event_interruptible_lock_irq_timeout( - fifo->write_queue, - ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) + ret = wait_event_interruptible_lock_irq_timeout + (fifo->write_queue, + ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write, - fifo->write_queue_lock, - (write_timeout >= 0) ? msecs_to_jiffies(write_timeout) : + fifo->write_queue_lock, + (write_timeout >= 0) ? msecs_to_jiffies(write_timeout) : MAX_SCHEDULE_TIMEOUT); spin_unlock_irq(&fifo->write_queue_lock); @@ -1089,6 +1087,8 @@ static int __init axis_fifo_init(void) pr_info("axis-fifo driver loaded with parameters read_timeout = %i, write_timeout = %i\n", read_timeout, write_timeout); axis_fifo_driver_class = class_create(THIS_MODULE, DRIVER_NAME); + if (IS_ERR(axis_fifo_driver_class)) + return PTR_ERR(axis_fifo_driver_class); return platform_driver_register(&axis_fifo_driver); } diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -199,10 +199,10 @@ static int clk_wzrd_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_disable_clk; } - clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor( - &pdev->dev, clk_name, - __clk_get_name(clk_wzrd->clk_in1), - 0, reg, 1); + clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor + (&pdev->dev, clk_name, + __clk_get_name(clk_wzrd->clk_in1), + 0, reg, 1); kfree(clk_name); if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { dev_err(&pdev->dev, "unable to register fixed-factor clock\n"); @@ -219,10 +219,10 @@ static int clk_wzrd_probe(struct platform_device *pdev) goto err_rm_int_clk; } - clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor( - &pdev->dev, clk_name, - __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]), - 0, 1, reg); + clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor + (&pdev->dev, clk_name, + __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]), + 0, 1, reg); if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) { dev_err(&pdev->dev, "unable to register divider clock\n"); ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]); @@ -243,8 +243,8 @@ static int clk_wzrd_probe(struct platform_device *pdev) reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2) + i * 12); reg &= WZRD_CLKOUT_DIVIDE_MASK; reg >>= WZRD_CLKOUT_DIVIDE_SHIFT; - clk_wzrd->clkout[i] = clk_register_fixed_factor(&pdev->dev, - clkout_name, clk_name, 0, 1, reg); + clk_wzrd->clkout[i] = clk_register_fixed_factor + (&pdev->dev, clkout_name, clk_name, 0, 1, reg); if (IS_ERR(clk_wzrd->clkout[i])) { int j; diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig @@ -1313,5 +1313,9 @@ config COMEDI_NI_LABPC_ISADMA config COMEDI_NI_TIO tristate + select COMEDI_NI_ROUTING + +config COMEDI_NI_ROUTING + tristate endif # COMEDI diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h @@ -107,6 +107,7 @@ #define INSN_WRITE (1 | INSN_MASK_WRITE) #define INSN_BITS (2 | INSN_MASK_READ | INSN_MASK_WRITE) #define INSN_CONFIG (3 | INSN_MASK_READ | INSN_MASK_WRITE) +#define INSN_DEVICE_CONFIG (INSN_CONFIG | INSN_MASK_SPECIAL) #define INSN_GTOD (4 | INSN_MASK_READ | INSN_MASK_SPECIAL) #define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) #define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) @@ -301,6 +302,8 @@ enum comedi_io_direction { * @INSN_CONFIG_PWM_SET_H_BRIDGE: Set PWM H bridge duty cycle and polarity for * a relay simultaneously. * @INSN_CONFIG_PWM_GET_H_BRIDGE: Get PWM H bridge duty cycle and polarity. + * @INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: Get the hardware timing restraints, + * regardless of trigger sources. */ enum configuration_ids { INSN_CONFIG_DIO_INPUT = COMEDI_INPUT, @@ -344,7 +347,25 @@ enum configuration_ids { INSN_CONFIG_PWM_GET_PERIOD = 5001, INSN_CONFIG_GET_PWM_STATUS = 5002, INSN_CONFIG_PWM_SET_H_BRIDGE = 5003, - INSN_CONFIG_PWM_GET_H_BRIDGE = 5004 + INSN_CONFIG_PWM_GET_H_BRIDGE = 5004, + INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS = 5005, +}; + +/** + * enum device_configuration_ids - COMEDI configuration instruction codes global + * to an entire device. + * @INSN_DEVICE_CONFIG_TEST_ROUTE: Validate the possibility of a + * globally-named route + * @INSN_DEVICE_CONFIG_CONNECT_ROUTE: Connect a globally-named route + * @INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:Disconnect a globally-named route + * @INSN_DEVICE_CONFIG_GET_ROUTES: Get a list of all globally-named routes + * that are valid for a particular device. + */ +enum device_config_route_ids { + INSN_DEVICE_CONFIG_TEST_ROUTE = 0, + INSN_DEVICE_CONFIG_CONNECT_ROUTE = 1, + INSN_DEVICE_CONFIG_DISCONNECT_ROUTE = 2, + INSN_DEVICE_CONFIG_GET_ROUTES = 3, }; /** @@ -928,6 +949,157 @@ enum i8254_mode { I8254_BINARY = 0 }; +/* *** BEGIN GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */ + +/* + * Common National Instruments Terminal/Signal names. + * Some of these have no NI_ prefix as they are useful for non-NI hardware, such + * as those that utilize the PXI/RTSI trigger lines. + * + * NOTE ABOUT THE CHOICE OF NAMES HERE AND THE CAMELSCRIPT: + * The choice to use CamelScript and the exact names below is for + * maintainability, clarity, similarity to manufacturer's documentation, + * _and_ a mitigation for confusion that has plagued the use of these drivers + * for years! + * + * More detail: + * There have been significant confusions over the past many years for users + * when trying to understand how to connect to/from signals and terminals on + * NI hardware using comedi. The major reason for this is that the actual + * register values were exposed and required to be used by users. Several + * major reasons exist why this caused major confusion for users: + * 1) The register values are _NOT_ in user documentation, but rather in + * arcane locations, such as a few register programming manuals that are + * increasingly hard to find and the NI MHDDK (comments in in example code). + * There is no one place to find the various valid values of the registers. + * 2) The register values are _NOT_ completely consistent. There is no way to + * gain any sense of intuition of which values, or even enums one should use + * for various registers. There was some attempt in prior use of comedi to + * name enums such that a user might know which enums should be used for + * varying purposes, but the end-user had to gain a knowledge of register + * values to correctly wield this approach. + * 3) The names for signals and registers found in the various register level + * programming manuals and vendor-provided documentation are _not_ even + * close to the same names that are in the end-user documentation. + * + * Similar, albeit less, confusion plagued NI's previous version of their own + * drivers. Earlier than 2003, NI greatly simplified the situation for users + * by releasing a new API that abstracted the names of signals/terminals to a + * common and intuitive set of names. + * + * The names below mirror the names chosen and well documented by NI. These + * names are exposed to the user via the comedilib user library. By keeping + * the names below, in spite of the use of CamelScript, maintenance will be + * greatly eased and confusion for users _and_ comedi developers will be + * greatly reduced. + */ + +/* + * Base of abstracted NI names. + * The first 16 bits of *_arg are reserved for channel selection. + * Since we only actually need the first 4 or 5 bits for all register values on + * NI select registers anyways, we'll identify all values >= (1<<15) as being an + * abstracted NI signal/terminal name. + * These values are also used/returned by INSN_DEVICE_CONFIG_TEST_ROUTE, + * INSN_DEVICE_CONFIG_CONNECT_ROUTE, INSN_DEVICE_CONFIG_DISCONNECT_ROUTE, + * and INSN_DEVICE_CONFIG_GET_ROUTES. + */ +#define NI_NAMES_BASE 0x8000u +/* + * not necessarily all allowed 64 PFIs are valid--certainly not for all devices + */ +#define NI_PFI(x) (NI_NAMES_BASE + ((x) & 0x3f)) +/* 8 trigger lines by standard, Some devices cannot talk to all eight. */ +#define TRIGGER_LINE(x) (NI_PFI(-1) + 1 + ((x) & 0x7)) +/* 4 RTSI shared MUXes to route signals to/from TRIGGER_LINES on NI hardware */ +#define NI_RTSI_BRD(x) (TRIGGER_LINE(-1) + 1 + ((x) & 0x3)) + +/* *** Counter/timer names : 8 counters max *** */ +#define NI_COUNTER_NAMES_BASE (NI_RTSI_BRD(-1) + 1) +#define NI_MAX_COUNTERS 7 +#define NI_CtrSource(x) (NI_COUNTER_NAMES_BASE + ((x) & NI_MAX_COUNTERS)) +/* Gate, Aux, A,B,Z are all treated, at times as gates */ +#define NI_GATES_NAMES_BASE (NI_CtrSource(-1) + 1) +#define NI_CtrGate(x) (NI_GATES_NAMES_BASE + ((x) & NI_MAX_COUNTERS)) +#define NI_CtrAux(x) (NI_CtrGate(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +#define NI_CtrA(x) (NI_CtrAux(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +#define NI_CtrB(x) (NI_CtrA(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +#define NI_CtrZ(x) (NI_CtrB(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +#define NI_GATES_NAMES_MAX NI_CtrZ(-1) +#define NI_CtrArmStartTrigger(x) (NI_CtrZ(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +#define NI_CtrInternalOutput(x) \ + (NI_CtrArmStartTrigger(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +/** external pin(s) labeled conveniently as Ctr<i>Out. */ +#define NI_CtrOut(x) (NI_CtrInternalOutput(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +/** For Buffered sampling of ctr -- x series capability. */ +#define NI_CtrSampleClock(x) (NI_CtrOut(-1) + 1 + ((x) & NI_MAX_COUNTERS)) +#define NI_COUNTER_NAMES_MAX NI_CtrSampleClock(-1) + +enum ni_common_signal_names { + /* PXI_Star: this is a non-NI-specific signal */ + PXI_Star = NI_COUNTER_NAMES_MAX + 1, + PXI_Clk10, + PXIe_Clk100, + NI_AI_SampleClock, + NI_AI_SampleClockTimebase, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_ConvertClockTimebase, + NI_AI_PauseTrigger, + NI_AI_HoldCompleteEvent, + NI_AI_HoldComplete, + NI_AI_ExternalMUXClock, + NI_AI_STOP, /* pulse signal that occurs when a update is finished(?) */ + NI_AO_SampleClock, + NI_AO_SampleClockTimebase, + NI_AO_StartTrigger, + NI_AO_PauseTrigger, + NI_DI_SampleClock, + NI_DI_SampleClockTimebase, + NI_DI_StartTrigger, + NI_DI_ReferenceTrigger, + NI_DI_PauseTrigger, + NI_DI_InputBufferFull, + NI_DI_ReadyForStartEvent, + NI_DI_ReadyForTransferEventBurst, + NI_DI_ReadyForTransferEventPipelined, + NI_DO_SampleClock, + NI_DO_SampleClockTimebase, + NI_DO_StartTrigger, + NI_DO_PauseTrigger, + NI_DO_OutputBufferFull, + NI_DO_DataActiveEvent, + NI_DO_ReadyForStartEvent, + NI_DO_ReadyForTransferEvent, + NI_MasterTimebase, + NI_20MHzTimebase, + NI_80MHzTimebase, + NI_100MHzTimebase, + NI_200MHzTimebase, + NI_100kHzTimebase, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + NI_AnalogComparisonEvent, + NI_WatchdogExpiredEvent, + NI_WatchdogExpirationTrigger, + NI_SCXI_Trig1, + NI_LogicLow, + NI_LogicHigh, + NI_ExternalStrobe, + NI_PFI_DO, + NI_CaseGround, + /* special internal signal used as variable source for RTSI bus: */ + NI_RGOUT0, + + /* just a name to make the next more convenient, regardless of above */ + _NI_NAMES_MAX_PLUS_1, + NI_NUM_NAMES = _NI_NAMES_MAX_PLUS_1 - NI_NAMES_BASE, +}; + +/* *** END GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */ + #define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x))) #define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c @@ -1216,6 +1216,10 @@ static int check_insn_config_length(struct comedi_insn *insn, if (insn->n == 6) return 0; break; + case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: + if (insn->n >= 4) + return 0; + break; /* * by default we allow the insn since we don't have checks for * all possible cases yet @@ -1230,6 +1234,57 @@ static int check_insn_config_length(struct comedi_insn *insn, return -EINVAL; } +static int check_insn_device_config_length(struct comedi_insn *insn, + unsigned int *data) +{ + if (insn->n < 1) + return -EINVAL; + + switch (data[0]) { + case INSN_DEVICE_CONFIG_TEST_ROUTE: + case INSN_DEVICE_CONFIG_CONNECT_ROUTE: + case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE: + if (insn->n == 3) + return 0; + break; + case INSN_DEVICE_CONFIG_GET_ROUTES: + /* + * Big enough for config_id and the length of the userland + * memory buffer. Additional length should be in factors of 2 + * to communicate any returned route pairs (source,destination). + */ + if (insn->n >= 2) + return 0; + break; + } + return -EINVAL; +} + +/** + * get_valid_routes() - Calls low-level driver get_valid_routes function to + * either return a count of valid routes to user, or copy + * of list of all valid device routes to buffer in + * userspace. + * @dev: comedi device pointer + * @data: data from user insn call. The length of the data must be >= 2. + * data[0] must contain the INSN_DEVICE_CONFIG config_id. + * data[1](input) contains the number of _pairs_ for which memory is + * allotted from the user. If the user specifies '0', then only + * the number of pairs available is returned. + * data[1](output) returns either the number of pairs available (if none + * where requested) or the number of _pairs_ that are copied back + * to the user. + * data[2::2] returns each (source, destination) pair. + * + * Return: -EINVAL if low-level driver does not allocate and return routes as + * expected. Returns 0 otherwise. + */ +static int get_valid_routes(struct comedi_device *dev, unsigned int *data) +{ + data[1] = dev->get_valid_routes(dev, data[1], data + 2); + return 0; +} + static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn, unsigned int *data, void *file) { @@ -1293,6 +1348,24 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn, if (ret >= 0) ret = 1; break; + case INSN_DEVICE_CONFIG: + ret = check_insn_device_config_length(insn, data); + if (ret) + break; + + if (data[0] == INSN_DEVICE_CONFIG_GET_ROUTES) { + /* + * data[1] should be the number of _pairs_ that + * the memory can hold. + */ + data[1] = (insn->n - 2) / 2; + ret = get_valid_routes(dev, data); + break; + } + + /* other global device config instructions. */ + ret = dev->insn_device_config(dev, insn, data); + break; default: dev_dbg(dev->class_dev, "invalid insn\n"); ret = -EINVAL; diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h @@ -516,6 +516,15 @@ struct comedi_driver { * called when @use_count changes from 0 to 1. * @close: Optional pointer to a function set by the low-level driver to be * called when @use_count changed from 1 to 0. + * @insn_device_config: Optional pointer to a handler for all sub-instructions + * except %INSN_DEVICE_CONFIG_GET_ROUTES of the %INSN_DEVICE_CONFIG + * instruction. If this is not initialized by the low-level driver, a + * default handler will be set during post-configuration. + * @get_valid_routes: Optional pointer to a handler for the + * %INSN_DEVICE_CONFIG_GET_ROUTES sub-instruction of the + * %INSN_DEVICE_CONFIG instruction set. If this is not initialized by the + * low-level driver, a default handler that copies zero routes back to the + * user will be used. * * This is the main control data structure for a COMEDI device (as far as the * COMEDI core is concerned). There are two groups of COMEDI devices - @@ -565,6 +574,11 @@ struct comedi_device { int (*open)(struct comedi_device *dev); void (*close)(struct comedi_device *dev); + int (*insn_device_config)(struct comedi_device *dev, + struct comedi_insn *insn, unsigned int *data); + unsigned int (*get_valid_routes)(struct comedi_device *dev, + unsigned int n_pairs, + unsigned int *pair_data); }; /* diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c @@ -211,6 +211,19 @@ static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s) return -EINVAL; } +static int insn_device_inval(struct comedi_device *dev, + struct comedi_insn *insn, unsigned int *data) +{ + return -EINVAL; +} + +static unsigned int get_zero_valid_routes(struct comedi_device *dev, + unsigned int n_pairs, + unsigned int *pair_data) +{ + return 0; +} + int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { @@ -652,6 +665,12 @@ static int __comedi_device_postconfig(struct comedi_device *dev) int ret; int i; + if (!dev->insn_device_config) + dev->insn_device_config = insn_device_inval; + + if (!dev->get_valid_routes) + dev->get_valid_routes = get_zero_valid_routes; + for (i = 0; i < dev->n_subdevices; i++) { s = &dev->subdevices[i]; diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile @@ -137,6 +137,33 @@ obj-$(CONFIG_COMEDI_VMK80XX) += vmk80xx.o obj-$(CONFIG_COMEDI_MITE) += mite.o obj-$(CONFIG_COMEDI_NI_TIO) += ni_tio.o obj-$(CONFIG_COMEDI_NI_TIOCMD) += ni_tiocmd.o +obj-$(CONFIG_COMEDI_NI_ROUTING) += ni_routing.o +ni_routing-objs += ni_routes.o \ + ni_routing/ni_route_values.o \ + ni_routing/ni_route_values/ni_660x.o \ + ni_routing/ni_route_values/ni_eseries.o \ + ni_routing/ni_route_values/ni_mseries.o \ + ni_routing/ni_device_routes.o \ + ni_routing/ni_device_routes/pxi-6030e.o \ + ni_routing/ni_device_routes/pci-6070e.o \ + ni_routing/ni_device_routes/pci-6220.o \ + ni_routing/ni_device_routes/pci-6221.o \ + ni_routing/ni_device_routes/pxi-6224.o \ + ni_routing/ni_device_routes/pxi-6225.o \ + ni_routing/ni_device_routes/pci-6229.o \ + ni_routing/ni_device_routes/pci-6251.o \ + ni_routing/ni_device_routes/pxi-6251.o \ + ni_routing/ni_device_routes/pxie-6251.o \ + ni_routing/ni_device_routes/pci-6254.o \ + ni_routing/ni_device_routes/pci-6259.o \ + ni_routing/ni_device_routes/pci-6534.o \ + ni_routing/ni_device_routes/pxie-6535.o \ + ni_routing/ni_device_routes/pci-6602.o \ + ni_routing/ni_device_routes/pci-6713.o \ + ni_routing/ni_device_routes/pci-6723.o \ + ni_routing/ni_device_routes/pci-6733.o \ + ni_routing/ni_device_routes/pxi-6733.o \ + ni_routing/ni_device_routes/pxie-6738.o obj-$(CONFIG_COMEDI_NI_LABPC) += ni_labpc_common.o obj-$(CONFIG_COMEDI_NI_LABPC_ISADMA) += ni_labpc_isadma.o @@ -145,3 +172,4 @@ obj-$(CONFIG_COMEDI_8255_SA) += 8255.o obj-$(CONFIG_COMEDI_AMPLC_DIO200) += amplc_dio200_common.o obj-$(CONFIG_COMEDI_AMPLC_PC236) += amplc_pc236_common.o obj-$(CONFIG_COMEDI_DAS08) += das08.o +obj-$(CONFIG_COMEDI_TESTS) += tests/ diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c @@ -626,6 +626,48 @@ static int waveform_ao_insn_write(struct comedi_device *dev, return insn->n; } +static int waveform_ai_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) { + /* + * input: data[1], data[2] : scan_begin_src, convert_src + * output: data[1], data[2] : scan_begin_min, convert_min + */ + if (data[1] == TRIG_FOLLOW) { + /* exactly TRIG_FOLLOW case */ + data[1] = 0; + data[2] = NSEC_PER_USEC; + } else { + data[1] = NSEC_PER_USEC; + if (data[2] & TRIG_TIMER) + data[2] = NSEC_PER_USEC; + else + data[2] = 0; + } + return 0; + } + + return -EINVAL; +} + +static int waveform_ao_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) { + /* we don't care about actual channels */ + data[1] = NSEC_PER_USEC; /* scan_begin_min */ + data[2] = 0; /* convert_min */ + return 0; + } + + return -EINVAL; +} + static int waveform_common_attach(struct comedi_device *dev, int amplitude, int period) { @@ -658,6 +700,7 @@ static int waveform_common_attach(struct comedi_device *dev, s->do_cmd = waveform_ai_cmd; s->do_cmdtest = waveform_ai_cmdtest; s->cancel = waveform_ai_cancel; + s->insn_config = waveform_ai_insn_config; s = &dev->subdevices[1]; dev->write_subdev = s; @@ -673,6 +716,7 @@ static int waveform_common_attach(struct comedi_device *dev, s->do_cmd = waveform_ao_cmd; s->do_cmdtest = waveform_ao_cmdtest; s->cancel = waveform_ao_cancel; + s->insn_config = waveform_ao_insn_config; /* Our default loopback value is just a 0V flatline */ for (i = 0; i < s->n_chan; i++) diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c @@ -7,7 +7,7 @@ * Driver: ni_660x * Description: National Instruments 660x counter/timer boards * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602, - * PXI-6608, PCI-6624, PXI-6624 + * PCI-6608, PXI-6608, PCI-6624, PXI-6624 * Author: J.P. Mellor <jpmellor@rose-hulman.edu>, * Herman.Bruyninckx@mech.kuleuven.ac.be, * Wim.Meeussen@mech.kuleuven.ac.be, @@ -31,6 +31,7 @@ #include "mite.h" #include "ni_tio.h" +#include "ni_routes.h" /* See Register-Level Programmer Manual page 3.1 */ enum ni_660x_register { @@ -201,6 +202,7 @@ enum ni_660x_boardid { BOARD_PCI6601, BOARD_PCI6602, BOARD_PXI6602, + BOARD_PCI6608, BOARD_PXI6608, BOARD_PCI6624, BOARD_PXI6624 @@ -224,6 +226,10 @@ static const struct ni_660x_board ni_660x_boards[] = { .name = "PXI-6602", .n_chips = 2, }, + [BOARD_PCI6608] = { + .name = "PCI-6608", + .n_chips = 2, + }, [BOARD_PXI6608] = { .name = "PXI-6608", .n_chips = 2, @@ -259,6 +265,7 @@ struct ni_660x_private { unsigned int dma_cfg[NI660X_MAX_CHIPS]; unsigned int io_cfg[NI660X_NUM_PFI_CHANNELS]; u64 io_dir; + struct ni_route_tables routing_tables; }; static void ni_660x_write(struct comedi_device *dev, unsigned int chip, @@ -561,6 +568,10 @@ static void ni_660x_select_pfi_output(struct comedi_device *dev, unsigned int idle_chip = 0; unsigned int bits; + if (chan >= NI_PFI(0)) + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + if (board->n_chips > 1) { if (out_sel == NI_660X_PFI_OUTPUT_COUNTER && chan >= 8 && chan <= 23) { @@ -589,11 +600,54 @@ static void ni_660x_select_pfi_output(struct comedi_device *dev, ni_660x_write(dev, active_chip, bits, NI660X_IO_CFG(chan)); } +static void ni_660x_set_pfi_direction(struct comedi_device *dev, + unsigned int chan, + unsigned int direction) +{ + struct ni_660x_private *devpriv = dev->private; + u64 bit; + + if (chan >= NI_PFI(0)) + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + + bit = 1ULL << chan; + + if (direction == COMEDI_OUTPUT) { + devpriv->io_dir |= bit; + /* reset the output to currently assigned output value */ + ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]); + } else { + devpriv->io_dir &= ~bit; + /* set pin to high-z; do not change currently assigned route */ + ni_660x_select_pfi_output(dev, chan, 0); + } +} + +static unsigned int ni_660x_get_pfi_direction(struct comedi_device *dev, + unsigned int chan) +{ + struct ni_660x_private *devpriv = dev->private; + u64 bit; + + if (chan >= NI_PFI(0)) + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + + bit = 1ULL << chan; + + return (devpriv->io_dir & bit) ? COMEDI_OUTPUT : COMEDI_INPUT; +} + static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned int chan, unsigned int source) { struct ni_660x_private *devpriv = dev->private; + if (chan >= NI_PFI(0)) + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + switch (source) { case NI_660X_PFI_OUTPUT_COUNTER: if (chan < 8) @@ -607,36 +661,56 @@ static int ni_660x_set_pfi_routing(struct comedi_device *dev, } devpriv->io_cfg[chan] = source; - if (devpriv->io_dir & (1ULL << chan)) + if (ni_660x_get_pfi_direction(dev, chan) == COMEDI_OUTPUT) ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]); return 0; } +static int ni_660x_get_pfi_routing(struct comedi_device *dev, unsigned int chan) +{ + struct ni_660x_private *devpriv = dev->private; + + if (chan >= NI_PFI(0)) + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + + return devpriv->io_cfg[chan]; +} + +static void ni_660x_set_pfi_filter(struct comedi_device *dev, + unsigned int chan, unsigned int value) +{ + unsigned int val; + + if (chan >= NI_PFI(0)) + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + + val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan)); + val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan); + val |= NI660X_IO_CFG_IN_SEL(chan, value); + ni_660x_write(dev, 0, val, NI660X_IO_CFG(chan)); +} + static int ni_660x_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ni_660x_private *devpriv = dev->private; unsigned int chan = CR_CHAN(insn->chanspec); - u64 bit = 1ULL << chan; - unsigned int val; int ret; switch (data[0]) { case INSN_CONFIG_DIO_OUTPUT: - devpriv->io_dir |= bit; - ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]); + ni_660x_set_pfi_direction(dev, chan, COMEDI_OUTPUT); break; case INSN_CONFIG_DIO_INPUT: - devpriv->io_dir &= ~bit; - ni_660x_select_pfi_output(dev, chan, 0); /* high-z */ + ni_660x_set_pfi_direction(dev, chan, COMEDI_INPUT); break; case INSN_CONFIG_DIO_QUERY: - data[1] = (devpriv->io_dir & bit) ? COMEDI_OUTPUT - : COMEDI_INPUT; + data[1] = ni_660x_get_pfi_direction(dev, chan); break; case INSN_CONFIG_SET_ROUTING: @@ -646,14 +720,11 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev, break; case INSN_CONFIG_GET_ROUTING: - data[1] = devpriv->io_cfg[chan]; + data[1] = ni_660x_get_pfi_routing(dev, chan); break; case INSN_CONFIG_FILTER: - val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan)); - val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan); - val |= NI660X_IO_CFG_IN_SEL(chan, data[1]); - ni_660x_write(dev, 0, val, NI660X_IO_CFG(chan)); + ni_660x_set_pfi_filter(dev, chan, data[1]); break; default: @@ -663,6 +734,240 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev, return insn->n; } +static unsigned int _ni_get_valid_routes(struct comedi_device *dev, + unsigned int n_pairs, + unsigned int *pair_data) +{ + struct ni_660x_private *devpriv = dev->private; + + return ni_get_valid_routes(&devpriv->routing_tables, n_pairs, + pair_data); +} + +/* + * Retrieves the current source of the output selector for the given + * destination. If the terminal for the destination is not already configured + * as an output, this function returns -EINVAL as error. + * + * Return: The register value of the destination output selector; + * -EINVAL if terminal is not configured for output. + */ +static inline int get_output_select_source(int dest, struct comedi_device *dev) +{ + struct ni_660x_private *devpriv = dev->private; + int reg = -1; + + if (channel_is_pfi(dest)) { + if (ni_660x_get_pfi_direction(dev, dest) == COMEDI_OUTPUT) + reg = ni_660x_get_pfi_routing(dev, dest); + } else if (channel_is_rtsi(dest)) { + dev_dbg(dev->class_dev, + "%s: unhandled rtsi destination (%d) queried\n", + __func__, dest); + /* + * The following can be enabled when RTSI routing info is + * determined (not currently documented): + * if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) { + * reg = ni_get_rtsi_routing(dev, dest); + + * if (reg == NI_RTSI_OUTPUT_RGOUT0) { + * dest = NI_RGOUT0; ** prepare for lookup below ** + * reg = get_rgout0_reg(dev); + * } else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) && + * reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) { + * const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0); + + * dest = NI_RTSI_BRD(i); ** prepare for lookup ** + * reg = get_ith_rtsi_brd_reg(i, dev); + * } + * } + */ + } else if (channel_is_ctr(dest)) { + reg = ni_tio_get_routing(devpriv->counter_dev, dest); + } else { + dev_dbg(dev->class_dev, + "%s: unhandled destination (%d) queried\n", + __func__, dest); + } + + if (reg >= 0) + return ni_find_route_source(CR_CHAN(reg), dest, + &devpriv->routing_tables); + return -EINVAL; +} + +/* + * Test a route: + * + * Return: -1 if not connectible; + * 0 if connectible and not connected; + * 1 if connectible and connected. + */ +static inline int test_route(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_660x_private *devpriv = dev->private; + s8 reg = ni_route_to_register(CR_CHAN(src), dest, + &devpriv->routing_tables); + + if (reg < 0) + return -1; + if (get_output_select_source(dest, dev) != CR_CHAN(src)) + return 0; + return 1; +} + +/* Connect the actual route. */ +static inline int connect_route(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_660x_private *devpriv = dev->private; + s8 reg = ni_route_to_register(CR_CHAN(src), dest, + &devpriv->routing_tables); + s8 current_src; + + if (reg < 0) + /* route is not valid */ + return -EINVAL; + + current_src = get_output_select_source(dest, dev); + if (current_src == CR_CHAN(src)) + return -EALREADY; + if (current_src >= 0) + /* destination mux is already busy. complain, don't overwrite */ + return -EBUSY; + + /* The route is valid and available. Now connect... */ + if (channel_is_pfi(CR_CHAN(dest))) { + /* + * set routing and then direction so that the output does not + * first get generated with the wrong pin + */ + ni_660x_set_pfi_routing(dev, dest, reg); + ni_660x_set_pfi_direction(dev, dest, COMEDI_OUTPUT); + } else if (channel_is_rtsi(CR_CHAN(dest))) { + dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n", + __func__, dest); + return -EINVAL; + /* + * The following can be enabled when RTSI routing info is + * determined (not currently documented): + * if (reg == NI_RTSI_OUTPUT_RGOUT0) { + * int ret = incr_rgout0_src_use(src, dev); + + * if (ret < 0) + * return ret; + * } else if (ni_rtsi_route_requires_mux(reg)) { + * ** Attempt to allocate and route (src->brd) ** + * int brd = incr_rtsi_brd_src_use(src, dev); + + * if (brd < 0) + * return brd; + + * ** Now lookup the register value for (brd->dest) ** + * reg = ni_lookup_route_register(brd, CR_CHAN(dest), + * &devpriv->routing_tables); + * } + + * ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT); + * ni_set_rtsi_routing(dev, dest, reg); + */ + } else if (channel_is_ctr(CR_CHAN(dest))) { + /* + * we are adding back the channel modifier info to set + * invert/edge info passed by the user + */ + ni_tio_set_routing(devpriv->counter_dev, dest, + reg | (src & ~CR_CHAN(-1))); + } else { + return -EINVAL; + } + return 0; +} + +static inline int disconnect_route(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_660x_private *devpriv = dev->private; + s8 reg = ni_route_to_register(CR_CHAN(src), CR_CHAN(dest), + &devpriv->routing_tables); + + if (reg < 0) + /* route is not valid */ + return -EINVAL; + if (get_output_select_source(dest, dev) != CR_CHAN(src)) + /* cannot disconnect something not connected */ + return -EINVAL; + + /* The route is valid and is connected. Now disconnect... */ + if (channel_is_pfi(CR_CHAN(dest))) { + unsigned int source = ((CR_CHAN(dest) - NI_PFI(0)) < 8) + ? NI_660X_PFI_OUTPUT_DIO + : NI_660X_PFI_OUTPUT_COUNTER; + + /* set the pfi to high impedance, and disconnect */ + ni_660x_set_pfi_direction(dev, dest, COMEDI_INPUT); + ni_660x_set_pfi_routing(dev, dest, source); + } else if (channel_is_rtsi(CR_CHAN(dest))) { + dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n", + __func__, dest); + return -EINVAL; + /* + * The following can be enabled when RTSI routing info is + * determined (not currently documented): + * if (reg == NI_RTSI_OUTPUT_RGOUT0) { + * int ret = decr_rgout0_src_use(src, dev); + + * if (ret < 0) + * return ret; + * } else if (ni_rtsi_route_requires_mux(reg)) { + * ** find which RTSI_BRD line is source for rtsi pin ** + * int brd = ni_find_route_source( + * ni_get_rtsi_routing(dev, dest), CR_CHAN(dest), + * &devpriv->routing_tables); + + * if (brd < 0) + * return brd; + + * ** decrement/disconnect RTSI_BRD line from source ** + * decr_rtsi_brd_src_use(src, brd, dev); + * } + + * ** set rtsi output selector to default state ** + * reg = default_rtsi_routing[CR_CHAN(dest) - TRIGGER_LINE(0)]; + * ni_set_rtsi_direction(dev, dest, COMEDI_INPUT); + * ni_set_rtsi_routing(dev, dest, reg); + */ + } else if (channel_is_ctr(CR_CHAN(dest))) { + ni_tio_unset_routing(devpriv->counter_dev, dest); + } else { + return -EINVAL; + } + return 0; +} + +static int ni_global_insn_config(struct comedi_device *dev, + struct comedi_insn *insn, + unsigned int *data) +{ + switch (data[0]) { + case INSN_DEVICE_CONFIG_TEST_ROUTE: + data[0] = test_route(data[1], data[2], dev); + return 2; + case INSN_DEVICE_CONFIG_CONNECT_ROUTE: + return connect_route(data[1], data[2], dev); + case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE: + return disconnect_route(data[1], data[2], dev); + /* + * This case is already handled one level up. + * case INSN_DEVICE_CONFIG_GET_ROUTES: + */ + default: + return -EINVAL; + } + return 1; +} + static void ni_660x_init_tio_chips(struct comedi_device *dev, unsigned int n_chips) { @@ -730,12 +1035,30 @@ static int ni_660x_auto_attach(struct comedi_device *dev, ni_660x_init_tio_chips(dev, board->n_chips); + /* prepare the device for globally-named routes. */ + if (ni_assign_device_routes("ni_660x", board->name, + &devpriv->routing_tables) < 0) { + dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", + __func__, board->name); + dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n", + __func__, board->name); + } else { + /* + * only(?) assign insn_device_config if we have global names for + * this device. + */ + dev->insn_device_config = ni_global_insn_config; + dev->get_valid_routes = _ni_get_valid_routes; + } + n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP; gpct_dev = ni_gpct_device_construct(dev, ni_660x_gpct_write, ni_660x_gpct_read, ni_gpct_variant_660x, - n_counters); + n_counters, + NI660X_COUNTERS_PER_CHIP, + &devpriv->routing_tables); if (!gpct_dev) return -ENOMEM; devpriv->counter_dev = gpct_dev; @@ -822,7 +1145,7 @@ static int ni_660x_auto_attach(struct comedi_device *dev, : NI_660X_PFI_OUTPUT_COUNTER; ni_660x_set_pfi_routing(dev, i, source); - ni_660x_select_pfi_output(dev, i, 0); /* high-z */ + ni_660x_set_pfi_direction(dev, i, COMEDI_INPUT);/* high-z */ } /* Counter subdevices (4 NI TIO General Purpose Counters per chip) */ @@ -831,9 +1154,6 @@ static int ni_660x_auto_attach(struct comedi_device *dev, if (i < n_counters) { struct ni_gpct *counter = &gpct_dev->counters[i]; - counter->chip_index = i / NI660X_COUNTERS_PER_CHIP; - counter->counter_index = i % NI660X_COUNTERS_PER_CHIP; - s->type = COMEDI_SUBD_COUNTER; s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_CMD_READ; @@ -915,6 +1235,7 @@ static const struct pci_device_id ni_660x_pci_table[] = { { PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 }, { PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 }, { PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 }, + { PCI_VDEVICE(NI, 0x2db0), BOARD_PCI6608 }, { PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 }, { PCI_VDEVICE(NI, 0x1e30), BOARD_PCI6624 }, { PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 }, diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -351,7 +351,8 @@ static const struct mio_regmap m_series_stc_write_regmap[] = { [NISTC_AO_PERSONAL_REG] = { 0x19c, 2 }, [NISTC_RTSI_TRIGA_OUT_REG] = { 0x19e, 2 }, [NISTC_RTSI_TRIGB_OUT_REG] = { 0x1a0, 2 }, - [NISTC_RTSI_BOARD_REG] = { 0, 0 }, /* Unknown */ + /* doc for following line: mhddk/nimseries/ChipObjects/tMSeries.h */ + [NISTC_RTSI_BOARD_REG] = { 0x1a2, 2 }, [NISTC_CFG_MEM_CLR_REG] = { 0x1a4, 2 }, [NISTC_ADC_FIFO_CLR_REG] = { 0x1a6, 2 }, [NISTC_DAC_FIFO_CLR_REG] = { 0x1a8, 2 }, @@ -2006,7 +2007,6 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, const struct ni_board_struct *board = dev->board_ptr; struct ni_private *devpriv = dev->private; int err = 0; - unsigned int tmp; unsigned int sources; /* Step 1 : check if triggers are trivially valid */ @@ -2047,12 +2047,9 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); break; case TRIG_EXT: - tmp = CR_CHAN(cmd->start_arg); - - if (tmp > 16) - tmp = 16; - tmp |= (cmd->start_arg & (CR_INVERT | CR_EDGE)); - err |= comedi_check_trigger_arg_is(&cmd->start_arg, tmp); + err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->start_arg), + NI_AI_StartTrigger, + &devpriv->routing_tables, 1); break; } @@ -2064,12 +2061,9 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, 0xffffff); } else if (cmd->scan_begin_src == TRIG_EXT) { /* external trigger */ - unsigned int tmp = CR_CHAN(cmd->scan_begin_arg); - - if (tmp > 16) - tmp = 16; - tmp |= (cmd->scan_begin_arg & (CR_INVERT | CR_EDGE)); - err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, tmp); + err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->scan_begin_arg), + NI_AI_SampleClock, + &devpriv->routing_tables, 1); } else { /* TRIG_OTHER */ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); } @@ -2087,12 +2081,9 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, } } else if (cmd->convert_src == TRIG_EXT) { /* external trigger */ - unsigned int tmp = CR_CHAN(cmd->convert_arg); - - if (tmp > 16) - tmp = 16; - tmp |= (cmd->convert_arg & (CR_ALT_FILTER | CR_INVERT)); - err |= comedi_check_trigger_arg_is(&cmd->convert_arg, tmp); + err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->convert_arg), + NI_AI_ConvertClock, + &devpriv->routing_tables, 1); } else if (cmd->convert_src == TRIG_NOW) { err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); } @@ -2118,7 +2109,7 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, /* step 4: fix up any arguments */ if (cmd->scan_begin_src == TRIG_TIMER) { - tmp = cmd->scan_begin_arg; + unsigned int tmp = cmd->scan_begin_arg; cmd->scan_begin_arg = ni_timer_to_ns(dev, ni_ns_to_timer(dev, cmd->scan_begin_arg, @@ -2128,7 +2119,7 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, } if (cmd->convert_src == TRIG_TIMER) { if (!devpriv->is_611x && !devpriv->is_6143) { - tmp = cmd->convert_arg; + unsigned int tmp = cmd->convert_arg; cmd->convert_arg = ni_timer_to_ns(dev, ni_ns_to_timer(dev, cmd->convert_arg, @@ -2206,8 +2197,10 @@ static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) NISTC_AI_TRIG_START1_SEL(0); break; case TRIG_EXT: - ai_trig |= NISTC_AI_TRIG_START1_SEL(CR_CHAN(cmd->start_arg) + - 1); + ai_trig |= NISTC_AI_TRIG_START1_SEL( + ni_get_reg_value_roffs(CR_CHAN(cmd->start_arg), + NI_AI_StartTrigger, + &devpriv->routing_tables, 1)); if (cmd->start_arg & CR_INVERT) ai_trig |= NISTC_AI_TRIG_START1_POLARITY; @@ -2317,8 +2310,10 @@ static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) (cmd->scan_begin_arg & ~CR_EDGE) != (cmd->convert_arg & ~CR_EDGE)) start_stop_select |= NISTC_AI_START_SYNC; - start_stop_select |= - NISTC_AI_START_SEL(1 + CR_CHAN(cmd->scan_begin_arg)); + start_stop_select |= NISTC_AI_START_SEL( + ni_get_reg_value_roffs(CR_CHAN(cmd->scan_begin_arg), + NI_AI_SampleClock, + &devpriv->routing_tables, 1)); ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG); break; } @@ -2346,8 +2341,10 @@ static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); break; case TRIG_EXT: - mode1 |= NISTC_AI_MODE1_CONVERT_SRC(1 + - CR_CHAN(cmd->convert_arg)); + mode1 |= NISTC_AI_MODE1_CONVERT_SRC( + ni_get_reg_value_roffs(CR_CHAN(cmd->convert_arg), + NI_AI_ConvertClock, + &devpriv->routing_tables, 1)); if ((cmd->convert_arg & CR_INVERT) == 0) mode1 |= NISTC_AI_MODE1_CONVERT_POLARITY; ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG); @@ -2464,6 +2461,7 @@ static int ni_ai_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { + const struct ni_board_struct *board = dev->board_ptr; struct ni_private *devpriv = dev->private; if (insn->n < 1) @@ -2498,6 +2496,15 @@ static int ni_ai_insn_config(struct comedi_device *dev, } } return 2; + case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: + /* we don't care about actual channels */ + /* data[3] : chanlist_len */ + data[1] = ni_min_ai_scan_period_ns(dev, data[3]); + if (devpriv->is_611x || devpriv->is_6143) + data[2] = 0; /* simultaneous output */ + else + data[2] = board->ai_speed; + return 0; default: break; } @@ -2834,6 +2841,11 @@ static int ni_ao_insn_config(struct comedi_device *dev, return 0; case INSN_CONFIG_ARM: return ni_ao_arm(dev, s); + case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: + /* we don't care about actual channels */ + data[1] = board->ao_speed; + data[2] = 0; + return 0; default: break; } @@ -2955,7 +2967,10 @@ static void ni_ao_cmd_set_trigger(struct comedi_device *dev, trigsel = NISTC_AO_TRIG_START1_EDGE | NISTC_AO_TRIG_START1_SYNC; } else { /* TRIG_EXT */ - trigsel = NISTC_AO_TRIG_START1_SEL(CR_CHAN(cmd->start_arg) + 1); + trigsel = NISTC_AO_TRIG_START1_SEL( + ni_get_reg_value_roffs(CR_CHAN(cmd->start_arg), + NI_AO_StartTrigger, + &devpriv->routing_tables, 1)); /* 0=active high, 1=active low. see daq-stc 3-24 (p186) */ if (cmd->start_arg & CR_INVERT) trigsel |= NISTC_AO_TRIG_START1_POLARITY; @@ -3117,7 +3132,9 @@ static void ni_ao_cmd_set_update(struct comedi_device *dev, /* FIXME: assert scan_begin_arg != 0, ret failure otherwise */ devpriv->ao_cmd2 |= NISTC_AO_CMD2_BC_GATE_ENA; devpriv->ao_mode1 |= NISTC_AO_MODE1_UPDATE_SRC( - CR_CHAN(cmd->scan_begin_arg)); + ni_get_reg_value(CR_CHAN(cmd->scan_begin_arg), + NI_AO_SampleClock, + &devpriv->routing_tables)); if (cmd->scan_begin_arg & CR_INVERT) devpriv->ao_mode1 |= NISTC_AO_MODE1_UPDATE_SRC_POLARITY; } @@ -3313,12 +3330,9 @@ static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); break; case TRIG_EXT: - tmp = CR_CHAN(cmd->start_arg); - - if (tmp > 18) - tmp = 18; - tmp |= (cmd->start_arg & (CR_INVERT | CR_EDGE)); - err |= comedi_check_trigger_arg_is(&cmd->start_arg, tmp); + err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->start_arg), + NI_AO_StartTrigger, + &devpriv->routing_tables, 1); break; } @@ -3328,6 +3342,10 @@ static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, devpriv->clock_ns * 0xffffff); + } else { /* TRIG_EXT */ + err |= ni_check_trigger_arg(CR_CHAN(cmd->scan_begin_arg), + NI_AO_SampleClock, + &devpriv->routing_tables); } err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); @@ -3475,6 +3493,15 @@ static int ni_m_series_dio_insn_config(struct comedi_device *dev, { int ret; + if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) { + const struct ni_board_struct *board = dev->board_ptr; + + /* we don't care about actual channels */ + data[1] = board->dio_speed; + data[2] = 0; + return 0; + } + ret = comedi_dio_insn_config(dev, s, insn, data, 0); if (ret) return ret; @@ -3516,8 +3543,8 @@ static int ni_cdio_check_chanlist(struct comedi_device *dev, static int ni_cdio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { + struct ni_private *devpriv = dev->private; int err = 0; - int tmp; /* Step 1 : check if triggers are trivially valid */ @@ -3537,9 +3564,15 @@ static int ni_cdio_cmdtest(struct comedi_device *dev, err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - tmp = cmd->scan_begin_arg; - tmp &= CR_PACK_FLAGS(NI_M_CDO_MODE_SAMPLE_SRC_MASK, 0, 0, CR_INVERT); - if (tmp != cmd->scan_begin_arg) + /* + * Although NI_D[IO]_SampleClock are the same, perhaps we should still, + * for completeness, test whether the cmd is output or input? + */ + err |= ni_check_trigger_arg(CR_CHAN(cmd->scan_begin_arg), + NI_DO_SampleClock, + &devpriv->routing_tables); + if (CR_RANGE(cmd->scan_begin_arg) != 0 || + CR_AREF(cmd->scan_begin_arg) != 0) err |= -EINVAL; err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); @@ -3627,9 +3660,16 @@ static int ni_cdio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) int retval; ni_writel(dev, NI_M_CDO_CMD_RESET, NI_M_CDIO_CMD_REG); + /* + * Although NI_D[IO]_SampleClock are the same, perhaps we should still, + * for completeness, test whether the cmd is output or input(?) + */ cdo_mode_bits = NI_M_CDO_MODE_FIFO_MODE | NI_M_CDO_MODE_HALT_ON_ERROR | - NI_M_CDO_MODE_SAMPLE_SRC(CR_CHAN(cmd->scan_begin_arg)); + NI_M_CDO_MODE_SAMPLE_SRC( + ni_get_reg_value(CR_CHAN(cmd->scan_begin_arg), + NI_DO_SampleClock, + &devpriv->routing_tables)); if (cmd->scan_begin_arg & CR_INVERT) cdo_mode_bits |= NI_M_CDO_MODE_POLARITY; ni_writel(dev, cdo_mode_bits, NI_M_CDO_MODE_REG); @@ -4551,24 +4591,33 @@ static unsigned int ni_get_pfi_routing(struct comedi_device *dev, { struct ni_private *devpriv = dev->private; + if (chan >= NI_PFI(0)) { + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + } return (devpriv->is_m_series) ? ni_m_series_get_pfi_routing(dev, chan) : ni_old_get_pfi_routing(dev, chan); } +/* Sets the output mux for the specified PFI channel. */ static int ni_set_pfi_routing(struct comedi_device *dev, unsigned int chan, unsigned int source) { struct ni_private *devpriv = dev->private; + if (chan >= NI_PFI(0)) { + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + } return (devpriv->is_m_series) ? ni_m_series_set_pfi_routing(dev, chan, source) : ni_old_set_pfi_routing(dev, chan, source); } -static int ni_config_filter(struct comedi_device *dev, - unsigned int pfi_channel, - enum ni_pfi_filter_select filter) +static int ni_config_pfi_filter(struct comedi_device *dev, + unsigned int chan, + enum ni_pfi_filter_select filter) { struct ni_private *devpriv = dev->private; unsigned int bits; @@ -4576,19 +4625,46 @@ static int ni_config_filter(struct comedi_device *dev, if (!devpriv->is_m_series) return -ENOTSUPP; + if (chan >= NI_PFI(0)) { + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + } + bits = ni_readl(dev, NI_M_PFI_FILTER_REG); - bits &= ~NI_M_PFI_FILTER_SEL_MASK(pfi_channel); - bits |= NI_M_PFI_FILTER_SEL(pfi_channel, filter); + bits &= ~NI_M_PFI_FILTER_SEL_MASK(chan); + bits |= NI_M_PFI_FILTER_SEL(chan, filter); ni_writel(dev, bits, NI_M_PFI_FILTER_REG); return 0; } +static void ni_set_pfi_direction(struct comedi_device *dev, int chan, + unsigned int direction) +{ + if (chan >= NI_PFI(0)) { + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + } + direction = (direction == COMEDI_OUTPUT) ? 1u : 0u; + ni_set_bits(dev, NISTC_IO_BIDIR_PIN_REG, 1 << chan, direction); +} + +static int ni_get_pfi_direction(struct comedi_device *dev, int chan) +{ + struct ni_private *devpriv = dev->private; + + if (chan >= NI_PFI(0)) { + /* allow new and old names of pfi channels to work. */ + chan -= NI_PFI(0); + } + return devpriv->io_bidirection_pin_reg & (1 << chan) ? + COMEDI_OUTPUT : COMEDI_INPUT; +} + static int ni_pfi_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ni_private *devpriv = dev->private; unsigned int chan; if (insn->n < 1) @@ -4598,23 +4674,19 @@ static int ni_pfi_insn_config(struct comedi_device *dev, switch (data[0]) { case COMEDI_OUTPUT: - ni_set_bits(dev, NISTC_IO_BIDIR_PIN_REG, 1 << chan, 1); - break; case COMEDI_INPUT: - ni_set_bits(dev, NISTC_IO_BIDIR_PIN_REG, 1 << chan, 0); + ni_set_pfi_direction(dev, chan, data[0]); break; case INSN_CONFIG_DIO_QUERY: - data[1] = - (devpriv->io_bidirection_pin_reg & (1 << chan)) ? - COMEDI_OUTPUT : COMEDI_INPUT; - return 0; + data[1] = ni_get_pfi_direction(dev, chan); + break; case INSN_CONFIG_SET_ROUTING: return ni_set_pfi_routing(dev, chan, data[1]); case INSN_CONFIG_GET_ROUTING: data[1] = ni_get_pfi_routing(dev, chan); break; case INSN_CONFIG_FILTER: - return ni_config_filter(dev, chan, data[1]); + return ni_config_pfi_filter(dev, chan, data[1]); default: return -EINVAL; } @@ -4980,7 +5052,10 @@ static int ni_valid_rtsi_output_source(struct comedi_device *dev, case NI_RTSI_OUTPUT_G_SRC0: case NI_RTSI_OUTPUT_G_GATE0: case NI_RTSI_OUTPUT_RGOUT0: - case NI_RTSI_OUTPUT_RTSI_BRD_0: + case NI_RTSI_OUTPUT_RTSI_BRD(0): + case NI_RTSI_OUTPUT_RTSI_BRD(1): + case NI_RTSI_OUTPUT_RTSI_BRD(2): + case NI_RTSI_OUTPUT_RTSI_BRD(3): return 1; case NI_RTSI_OUTPUT_RTSI_OSC: return (devpriv->is_m_series) ? 1 : 0; @@ -4994,6 +5069,10 @@ static int ni_set_rtsi_routing(struct comedi_device *dev, { struct ni_private *devpriv = dev->private; + if (chan >= TRIGGER_LINE(0)) + /* allow new and old names of rtsi channels to work. */ + chan -= TRIGGER_LINE(0); + if (ni_valid_rtsi_output_source(dev, chan, src) == 0) return -EINVAL; if (chan < 4) { @@ -5001,11 +5080,18 @@ static int ni_set_rtsi_routing(struct comedi_device *dev, devpriv->rtsi_trig_a_output_reg |= NISTC_RTSI_TRIG(chan, src); ni_stc_writew(dev, devpriv->rtsi_trig_a_output_reg, NISTC_RTSI_TRIGA_OUT_REG); - } else if (chan < 8) { + } else if (chan < NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { devpriv->rtsi_trig_b_output_reg &= ~NISTC_RTSI_TRIG_MASK(chan); devpriv->rtsi_trig_b_output_reg |= NISTC_RTSI_TRIG(chan, src); ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg, NISTC_RTSI_TRIGB_OUT_REG); + } else if (chan != NISTC_RTSI_TRIG_OLD_CLK_CHAN) { + /* probably should never reach this, since the + * ni_valid_rtsi_output_source above errors out if chan is too + * high + */ + dev_err(dev->class_dev, "%s: unknown rtsi channel\n", __func__); + return -EINVAL; } return 2; } @@ -5015,31 +5101,35 @@ static unsigned int ni_get_rtsi_routing(struct comedi_device *dev, { struct ni_private *devpriv = dev->private; + if (chan >= TRIGGER_LINE(0)) + /* allow new and old names of rtsi channels to work. */ + chan -= TRIGGER_LINE(0); + if (chan < 4) { return NISTC_RTSI_TRIG_TO_SRC(chan, devpriv->rtsi_trig_a_output_reg); } else if (chan < NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { return NISTC_RTSI_TRIG_TO_SRC(chan, devpriv->rtsi_trig_b_output_reg); - } else { - if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) - return NI_RTSI_OUTPUT_RTSI_OSC; - dev_err(dev->class_dev, "bug! should never get here?\n"); - return 0; + } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { + return NI_RTSI_OUTPUT_RTSI_OSC; } + + dev_err(dev->class_dev, "%s: unknown rtsi channel\n", __func__); + return -EINVAL; } -static int ni_rtsi_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) +static void ni_set_rtsi_direction(struct comedi_device *dev, int chan, + unsigned int direction) { struct ni_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); unsigned int max_chan = NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series); - switch (data[0]) { - case INSN_CONFIG_DIO_OUTPUT: + if (chan >= TRIGGER_LINE(0)) + /* allow new and old names of rtsi channels to work. */ + chan -= TRIGGER_LINE(0); + + if (direction == COMEDI_OUTPUT) { if (chan < max_chan) { devpriv->rtsi_trig_direction_reg |= NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series); @@ -5047,10 +5137,7 @@ static int ni_rtsi_insn_config(struct comedi_device *dev, devpriv->rtsi_trig_direction_reg |= NISTC_RTSI_TRIG_DRV_CLK; } - ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, - NISTC_RTSI_TRIG_DIR_REG); - break; - case INSN_CONFIG_DIO_INPUT: + } else { if (chan < max_chan) { devpriv->rtsi_trig_direction_reg &= ~NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series); @@ -5058,23 +5145,53 @@ static int ni_rtsi_insn_config(struct comedi_device *dev, devpriv->rtsi_trig_direction_reg &= ~NISTC_RTSI_TRIG_DRV_CLK; } - ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, - NISTC_RTSI_TRIG_DIR_REG); + } + ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, + NISTC_RTSI_TRIG_DIR_REG); +} + +static int ni_get_rtsi_direction(struct comedi_device *dev, int chan) +{ + struct ni_private *devpriv = dev->private; + unsigned int max_chan = NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series); + + if (chan >= TRIGGER_LINE(0)) + /* allow new and old names of rtsi channels to work. */ + chan -= TRIGGER_LINE(0); + + if (chan < max_chan) { + return (devpriv->rtsi_trig_direction_reg & + NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series)) + ? COMEDI_OUTPUT : COMEDI_INPUT; + } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { + return (devpriv->rtsi_trig_direction_reg & + NISTC_RTSI_TRIG_DRV_CLK) + ? COMEDI_OUTPUT : COMEDI_INPUT; + } + return -EINVAL; +} + +static int ni_rtsi_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct ni_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + + switch (data[0]) { + case COMEDI_OUTPUT: + case COMEDI_INPUT: + ni_set_rtsi_direction(dev, chan, data[0]); break; - case INSN_CONFIG_DIO_QUERY: - if (chan < max_chan) { - data[1] = - (devpriv->rtsi_trig_direction_reg & - NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series)) - ? INSN_CONFIG_DIO_OUTPUT - : INSN_CONFIG_DIO_INPUT; - } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { - data[1] = (devpriv->rtsi_trig_direction_reg & - NISTC_RTSI_TRIG_DRV_CLK) - ? INSN_CONFIG_DIO_OUTPUT - : INSN_CONFIG_DIO_INPUT; - } + case INSN_CONFIG_DIO_QUERY: { + int ret = ni_get_rtsi_direction(dev, chan); + + if (ret < 0) + return ret; + data[1] = ret; return 2; + } case INSN_CONFIG_SET_CLOCK_SRC: return ni_set_master_clock(dev, data[1], data[2]); case INSN_CONFIG_GET_CLOCK_SRC: @@ -5083,9 +5200,14 @@ static int ni_rtsi_insn_config(struct comedi_device *dev, return 3; case INSN_CONFIG_SET_ROUTING: return ni_set_rtsi_routing(dev, chan, data[1]); - case INSN_CONFIG_GET_ROUTING: - data[1] = ni_get_rtsi_routing(dev, chan); + case INSN_CONFIG_GET_ROUTING: { + int ret = ni_get_rtsi_routing(dev, chan); + + if (ret < 0) + return ret; + data[1] = ret; return 2; + } default: return -EINVAL; } @@ -5102,9 +5224,275 @@ static int ni_rtsi_insn_bits(struct comedi_device *dev, return insn->n; } +/* + * Default routing for RTSI trigger lines. + * + * These values are used here in the init function, as well as in the + * disconnect_route function, after a RTSI route has been disconnected. + */ +static const int default_rtsi_routing[] = { + [0] = NI_RTSI_OUTPUT_ADR_START1, + [1] = NI_RTSI_OUTPUT_ADR_START2, + [2] = NI_RTSI_OUTPUT_SCLKG, + [3] = NI_RTSI_OUTPUT_DACUPDN, + [4] = NI_RTSI_OUTPUT_DA_START1, + [5] = NI_RTSI_OUTPUT_G_SRC0, + [6] = NI_RTSI_OUTPUT_G_GATE0, + [7] = NI_RTSI_OUTPUT_RTSI_OSC, +}; + +/* + * Route signals through RGOUT0 terminal. + * @reg: raw register value of RGOUT0 bits (only bit0 is important). + * @dev: comedi device handle. + */ +static void set_rgout0_reg(int reg, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + + if (devpriv->is_m_series) { + devpriv->rtsi_trig_direction_reg &= + ~NISTC_RTSI_TRIG_DIR_SUB_SEL1; + devpriv->rtsi_trig_direction_reg |= + (reg << NISTC_RTSI_TRIG_DIR_SUB_SEL1_SHIFT) & + NISTC_RTSI_TRIG_DIR_SUB_SEL1; + ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, + NISTC_RTSI_TRIG_DIR_REG); + } else { + devpriv->rtsi_trig_b_output_reg &= ~NISTC_RTSI_TRIGB_SUB_SEL1; + devpriv->rtsi_trig_b_output_reg |= + (reg << NISTC_RTSI_TRIGB_SUB_SEL1_SHIFT) & + NISTC_RTSI_TRIGB_SUB_SEL1; + ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg, + NISTC_RTSI_TRIGB_OUT_REG); + } +} + +static int get_rgout0_reg(struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int reg; + + if (devpriv->is_m_series) + reg = (devpriv->rtsi_trig_direction_reg & + NISTC_RTSI_TRIG_DIR_SUB_SEL1) + >> NISTC_RTSI_TRIG_DIR_SUB_SEL1_SHIFT; + else + reg = (devpriv->rtsi_trig_b_output_reg & + NISTC_RTSI_TRIGB_SUB_SEL1) + >> NISTC_RTSI_TRIGB_SUB_SEL1_SHIFT; + return reg; +} + +static inline int get_rgout0_src(struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int reg = get_rgout0_reg(dev); + + return ni_find_route_source(reg, NI_RGOUT0, &devpriv->routing_tables); +} + +/* + * Route signals through RGOUT0 terminal and increment the RGOUT0 use for this + * particular route. + * @src: device-global signal name + * @dev: comedi device handle + * + * Return: -EINVAL if the source is not valid to route to RGOUT0; + * -EBUSY if the RGOUT0 is already used; + * 0 if successful. + */ +static int incr_rgout0_src_use(int src, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + s8 reg = ni_lookup_route_register(CR_CHAN(src), NI_RGOUT0, + &devpriv->routing_tables); + + if (reg < 0) + return -EINVAL; + + if (devpriv->rgout0_usage > 0 && get_rgout0_reg(dev) != reg) + return -EBUSY; + + ++devpriv->rgout0_usage; + set_rgout0_reg(reg, dev); + return 0; +} + +/* + * Unroute signals through RGOUT0 terminal and deccrement the RGOUT0 use for + * this particular source. This function does not actually unroute anything + * with respect to RGOUT0. It does, on the other hand, decrement the usage + * counter for the current src->RGOUT0 mapping. + * + * Return: -EINVAL if the source is not already routed to RGOUT0 (or usage is + * already at zero); 0 if successful. + */ +static int decr_rgout0_src_use(int src, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + s8 reg = ni_lookup_route_register(CR_CHAN(src), NI_RGOUT0, + &devpriv->routing_tables); + + if (devpriv->rgout0_usage > 0 && get_rgout0_reg(dev) == reg) { + --devpriv->rgout0_usage; + if (!devpriv->rgout0_usage) + set_rgout0_reg(0, dev); /* ok default? */ + return 0; + } + return -EINVAL; +} + +/* + * Route signals through given NI_RTSI_BRD mux. + * @i: index of mux to route + * @reg: raw register value of RTSI_BRD bits + * @dev: comedi device handle + */ +static void set_ith_rtsi_brd_reg(int i, int reg, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int reg_i_sz = 3; /* value for e-series */ + int reg_i_mask; + int reg_i_shift; + + if (devpriv->is_m_series) + reg_i_sz = 4; + reg_i_mask = ~((~0) << reg_i_sz); + reg_i_shift = i * reg_i_sz; + + /* clear out the current reg_i for ith brd */ + devpriv->rtsi_shared_mux_reg &= ~(reg_i_mask << reg_i_shift); + /* (softcopy) write the new reg_i for ith brd */ + devpriv->rtsi_shared_mux_reg |= (reg & reg_i_mask) << reg_i_shift; + /* (hardcopy) write the new reg_i for ith brd */ + ni_stc_writew(dev, devpriv->rtsi_shared_mux_reg, NISTC_RTSI_BOARD_REG); +} + +static int get_ith_rtsi_brd_reg(int i, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int reg_i_sz = 3; /* value for e-series */ + int reg_i_mask; + int reg_i_shift; + + if (devpriv->is_m_series) + reg_i_sz = 4; + reg_i_mask = ~((~0) << reg_i_sz); + reg_i_shift = i * reg_i_sz; + + return (devpriv->rtsi_shared_mux_reg >> reg_i_shift) & reg_i_mask; +} + +static inline int get_rtsi_brd_src(int brd, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int brd_index = brd; + int reg; + + if (brd >= NI_RTSI_BRD(0)) + brd_index = brd - NI_RTSI_BRD(0); + else + brd = NI_RTSI_BRD(brd); + /* + * And now: + * brd : device-global name + * brd_index : index number of RTSI_BRD mux + */ + + reg = get_ith_rtsi_brd_reg(brd_index, dev); + + return ni_find_route_source(reg, brd, &devpriv->routing_tables); +} + +/* + * Route signals through NI_RTSI_BRD mux and increment the use counter for this + * particular route. + * + * Return: -EINVAL if the source is not valid to route to NI_RTSI_BRD(i); + * -EBUSY if all NI_RTSI_BRD muxes are already used; + * NI_RTSI_BRD(i) of allocated ith mux if successful. + */ +static int incr_rtsi_brd_src_use(int src, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int first_available = -1; + int err = -EINVAL; + s8 reg; + int i; + + /* first look for a mux that is already configured to provide src */ + for (i = 0; i < NUM_RTSI_SHARED_MUXS; ++i) { + reg = ni_lookup_route_register(CR_CHAN(src), NI_RTSI_BRD(i), + &devpriv->routing_tables); + + if (reg < 0) + continue; /* invalid route */ + + if (!devpriv->rtsi_shared_mux_usage[i]) { + if (first_available < 0) + /* found the first unused, but usable mux */ + first_available = i; + } else { + /* + * we've seen at least one possible route, so change the + * final error to -EBUSY in case there are no muxes + * available. + */ + err = -EBUSY; + + if (get_ith_rtsi_brd_reg(i, dev) == reg) { + /* + * we've found a mux that is already being used + * to provide the requested signal. Reuse it. + */ + goto success; + } + } + } + + if (first_available < 0) + return err; + + /* we did not find a mux to reuse, but there is at least one usable */ + i = first_available; + +success: + ++devpriv->rtsi_shared_mux_usage[i]; + set_ith_rtsi_brd_reg(i, reg, dev); + return NI_RTSI_BRD(i); +} + +/* + * Unroute signals through NI_RTSI_BRD mux and decrement the user counter for + * this particular route. + * + * Return: -EINVAL if the source is not already routed to rtsi_brd(i) (or usage + * is already at zero); 0 if successful. + */ +static int decr_rtsi_brd_src_use(int src, int rtsi_brd, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + s8 reg = ni_lookup_route_register(CR_CHAN(src), rtsi_brd, + &devpriv->routing_tables); + const int i = rtsi_brd - NI_RTSI_BRD(0); + + if (devpriv->rtsi_shared_mux_usage[i] > 0 && + get_ith_rtsi_brd_reg(i, dev) == reg) { + --devpriv->rtsi_shared_mux_usage[i]; + if (!devpriv->rtsi_shared_mux_usage[i]) + set_ith_rtsi_brd_reg(i, 0, dev); /* ok default? */ + return 0; + } + + return -EINVAL; +} + static void ni_rtsi_init(struct comedi_device *dev) { struct ni_private *devpriv = dev->private; + int i; /* Initialises the RTSI bus signal switch to a default state */ @@ -5117,28 +5505,328 @@ static void ni_rtsi_init(struct comedi_device *dev) /* Set clock mode to internal */ if (ni_set_master_clock(dev, NI_MIO_INTERNAL_CLOCK, 0) < 0) dev_err(dev->class_dev, "ni_set_master_clock failed, bug?\n"); - /* default internal lines routing to RTSI bus lines */ - devpriv->rtsi_trig_a_output_reg = - NISTC_RTSI_TRIG(0, NI_RTSI_OUTPUT_ADR_START1) | - NISTC_RTSI_TRIG(1, NI_RTSI_OUTPUT_ADR_START2) | - NISTC_RTSI_TRIG(2, NI_RTSI_OUTPUT_SCLKG) | - NISTC_RTSI_TRIG(3, NI_RTSI_OUTPUT_DACUPDN); - ni_stc_writew(dev, devpriv->rtsi_trig_a_output_reg, - NISTC_RTSI_TRIGA_OUT_REG); - devpriv->rtsi_trig_b_output_reg = - NISTC_RTSI_TRIG(4, NI_RTSI_OUTPUT_DA_START1) | - NISTC_RTSI_TRIG(5, NI_RTSI_OUTPUT_G_SRC0) | - NISTC_RTSI_TRIG(6, NI_RTSI_OUTPUT_G_GATE0); - if (devpriv->is_m_series) - devpriv->rtsi_trig_b_output_reg |= - NISTC_RTSI_TRIG(7, NI_RTSI_OUTPUT_RTSI_OSC); - ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg, - NISTC_RTSI_TRIGB_OUT_REG); + /* default internal lines routing to RTSI bus lines */ + for (i = 0; i < 8; ++i) { + ni_set_rtsi_direction(dev, i, COMEDI_INPUT); + ni_set_rtsi_routing(dev, i, default_rtsi_routing[i]); + } + + /* + * Sets the source and direction of the 4 on board lines. + * This configures all board lines to be: + * for e-series: + * 1) inputs (not sure what "output" would mean) + * 2) copying TRIGGER_LINE(0) (or RTSI0) output + * for m-series: + * copying NI_PFI(0) output + */ + devpriv->rtsi_shared_mux_reg = 0; + for (i = 0; i < 4; ++i) + set_ith_rtsi_brd_reg(i, 0, dev); + memset(devpriv->rtsi_shared_mux_usage, 0, + sizeof(devpriv->rtsi_shared_mux_usage)); + + /* initialize rgout0 pin as unused. */ + devpriv->rgout0_usage = 0; + set_rgout0_reg(0, dev); +} + +/* Get route of GPFO_i/CtrOut pins */ +static inline int ni_get_gout_routing(unsigned int dest, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + unsigned int reg = devpriv->an_trig_etc_reg; + + switch (dest) { + case 0: + if (reg & NISTC_ATRIG_ETC_GPFO_0_ENA) + return NISTC_ATRIG_ETC_GPFO_0_SEL_TO_SRC(reg); + break; + case 1: + if (reg & NISTC_ATRIG_ETC_GPFO_1_ENA) + return NISTC_ATRIG_ETC_GPFO_1_SEL_TO_SRC(reg); + break; + } + + return -EINVAL; +} + +/* Set route of GPFO_i/CtrOut pins */ +static inline int ni_disable_gout_routing(unsigned int dest, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + + switch (dest) { + case 0: + devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_0_ENA; + break; + case 1: + devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_1_ENA; + break; + default: + return -EINVAL; + } + + ni_stc_writew(dev, devpriv->an_trig_etc_reg, NISTC_ATRIG_ETC_REG); + return 0; +} + +/* Set route of GPFO_i/CtrOut pins */ +static inline int ni_set_gout_routing(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + + switch (dest) { + case 0: + /* clear reg */ + devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_0_SEL(-1); + /* set reg */ + devpriv->an_trig_etc_reg |= NISTC_ATRIG_ETC_GPFO_0_ENA + | NISTC_ATRIG_ETC_GPFO_0_SEL(src); + break; + case 1: + /* clear reg */ + devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_1_SEL; + src = src ? NISTC_ATRIG_ETC_GPFO_1_SEL : 0; + /* set reg */ + devpriv->an_trig_etc_reg |= NISTC_ATRIG_ETC_GPFO_1_ENA | src; + break; + default: + return -EINVAL; + } + + ni_stc_writew(dev, devpriv->an_trig_etc_reg, NISTC_ATRIG_ETC_REG); + return 0; +} + +/* + * Retrieves the current source of the output selector for the given + * destination. If the terminal for the destination is not already configured + * as an output, this function returns -EINVAL as error. + * + * Return: the register value of the destination output selector; + * -EINVAL if terminal is not configured for output. + */ +static int get_output_select_source(int dest, struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + int reg = -1; + + if (channel_is_pfi(dest)) { + if (ni_get_pfi_direction(dev, dest) == COMEDI_OUTPUT) + reg = ni_get_pfi_routing(dev, dest); + } else if (channel_is_rtsi(dest)) { + if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) { + reg = ni_get_rtsi_routing(dev, dest); + + if (reg == NI_RTSI_OUTPUT_RGOUT0) { + dest = NI_RGOUT0; /* prepare for lookup below */ + reg = get_rgout0_reg(dev); + } else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) && + reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) { + const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0); + + dest = NI_RTSI_BRD(i); /* prepare for lookup */ + reg = get_ith_rtsi_brd_reg(i, dev); + } + } + } else if (dest >= NI_CtrOut(0) && dest <= NI_CtrOut(-1)) { + /* + * not handled by ni_tio. Only available for GPFO registers in + * e/m series. + */ + dest -= NI_CtrOut(0); + if (dest > 1) + /* there are only two g_out outputs. */ + return -EINVAL; + reg = ni_get_gout_routing(dest, dev); + } else if (channel_is_ctr(dest)) { + reg = ni_tio_get_routing(devpriv->counter_dev, dest); + } else { + dev_dbg(dev->class_dev, "%s: unhandled destination (%d) queried\n", + __func__, dest); + } + + if (reg >= 0) + return ni_find_route_source(CR_CHAN(reg), dest, + &devpriv->routing_tables); + return -EINVAL; +} + +/* + * Test a route: + * + * Return: -1 if not connectible; + * 0 if connectible and not connected; + * 1 if connectible and connected. + */ +static int test_route(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + s8 reg = ni_route_to_register(CR_CHAN(src), dest, + &devpriv->routing_tables); + + if (reg < 0) + return -1; + if (get_output_select_source(dest, dev) != CR_CHAN(src)) + return 0; + return 1; +} + +/* Connect the actual route. */ +static int connect_route(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + s8 reg = ni_route_to_register(CR_CHAN(src), dest, + &devpriv->routing_tables); + s8 current_src; + + if (reg < 0) + /* route is not valid */ + return -EINVAL; + + current_src = get_output_select_source(dest, dev); + if (current_src == CR_CHAN(src)) + return -EALREADY; + if (current_src >= 0) + /* destination mux is already busy. complain, don't overwrite */ + return -EBUSY; + + /* The route is valid and available. Now connect... */ + if (channel_is_pfi(dest)) { + /* set routing source, then open output */ + ni_set_pfi_routing(dev, dest, reg); + ni_set_pfi_direction(dev, dest, COMEDI_OUTPUT); + } else if (channel_is_rtsi(dest)) { + if (reg == NI_RTSI_OUTPUT_RGOUT0) { + int ret = incr_rgout0_src_use(src, dev); + + if (ret < 0) + return ret; + } else if (ni_rtsi_route_requires_mux(reg)) { + /* Attempt to allocate and route (src->brd) */ + int brd = incr_rtsi_brd_src_use(src, dev); + + if (brd < 0) + return brd; + + /* Now lookup the register value for (brd->dest) */ + reg = ni_lookup_route_register( + brd, dest, &devpriv->routing_tables); + } + + ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT); + ni_set_rtsi_routing(dev, dest, reg); + } else if (dest >= NI_CtrOut(0) && dest <= NI_CtrOut(-1)) { + /* + * not handled by ni_tio. Only available for GPFO registers in + * e/m series. + */ + dest -= NI_CtrOut(0); + if (dest > 1) + /* there are only two g_out outputs. */ + return -EINVAL; + if (ni_set_gout_routing(src, dest, dev)) + return -EINVAL; + } else if (channel_is_ctr(dest)) { + /* + * we are adding back the channel modifier info to set + * invert/edge info passed by the user + */ + ni_tio_set_routing(devpriv->counter_dev, dest, + reg | (src & ~CR_CHAN(-1))); + } else { + return -EINVAL; + } + return 0; +} + +static int disconnect_route(unsigned int src, unsigned int dest, + struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + s8 reg = ni_route_to_register(CR_CHAN(src), dest, + &devpriv->routing_tables); + + if (reg < 0) + /* route is not valid */ + return -EINVAL; + if (get_output_select_source(dest, dev) != src) + /* cannot disconnect something not connected */ + return -EINVAL; + + /* The route is valid and is connected. Now disconnect... */ + if (channel_is_pfi(dest)) { + /* set the pfi to high impedance, and disconnect */ + ni_set_pfi_direction(dev, dest, COMEDI_INPUT); + ni_set_pfi_routing(dev, dest, NI_PFI_OUTPUT_PFI_DEFAULT); + } else if (channel_is_rtsi(dest)) { + if (reg == NI_RTSI_OUTPUT_RGOUT0) { + int ret = decr_rgout0_src_use(src, dev); + + if (ret < 0) + return ret; + } else if (ni_rtsi_route_requires_mux(reg)) { + /* find which RTSI_BRD line is source for rtsi pin */ + int brd = ni_find_route_source( + ni_get_rtsi_routing(dev, dest), dest, + &devpriv->routing_tables); + + if (brd < 0) + return brd; + + /* decrement/disconnect RTSI_BRD line from source */ + decr_rtsi_brd_src_use(src, brd, dev); + } + + /* set rtsi output selector to default state */ + reg = default_rtsi_routing[dest - TRIGGER_LINE(0)]; + ni_set_rtsi_direction(dev, dest, COMEDI_INPUT); + ni_set_rtsi_routing(dev, dest, reg); + } else if (dest >= NI_CtrOut(0) && dest <= NI_CtrOut(-1)) { + /* + * not handled by ni_tio. Only available for GPFO registers in + * e/m series. + */ + dest -= NI_CtrOut(0); + if (dest > 1) + /* there are only two g_out outputs. */ + return -EINVAL; + reg = ni_disable_gout_routing(dest, dev); + } else if (channel_is_ctr(dest)) { + ni_tio_unset_routing(devpriv->counter_dev, dest); + } else { + return -EINVAL; + } + return 0; +} + +static int ni_global_insn_config(struct comedi_device *dev, + struct comedi_insn *insn, + unsigned int *data) +{ + switch (data[0]) { + case INSN_DEVICE_CONFIG_TEST_ROUTE: + data[0] = test_route(data[1], data[2], dev); + return 2; + case INSN_DEVICE_CONFIG_CONNECT_ROUTE: + return connect_route(data[1], data[2], dev); + case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE: + return disconnect_route(data[1], data[2], dev); /* - * Sets the source and direction of the 4 on board lines - * ni_stc_writew(dev, 0, NISTC_RTSI_BOARD_REG); + * This case is already handled one level up. + * case INSN_DEVICE_CONFIG_GET_ROUTES: */ + default: + return -EINVAL; + } + return 1; } #ifdef PCIDMA @@ -5244,6 +5932,16 @@ static int ni_alloc_private(struct comedi_device *dev) return 0; } +static unsigned int _ni_get_valid_routes(struct comedi_device *dev, + unsigned int n_pairs, + unsigned int *pair_data) +{ + struct ni_private *devpriv = dev->private; + + return ni_get_valid_routes(&devpriv->routing_tables, n_pairs, + pair_data); +} + static int ni_E_init(struct comedi_device *dev, unsigned int interrupt_pin, unsigned int irq_polarity) { @@ -5252,6 +5950,24 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_subdevice *s; int ret; int i; + const char *dev_family = devpriv->is_m_series ? "ni_mseries" + : "ni_eseries"; + + /* prepare the device for globally-named routes. */ + if (ni_assign_device_routes(dev_family, board->name, + &devpriv->routing_tables) < 0) { + dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", + __func__, board->name); + dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n", + __func__, board->name); + } else { + /* + * only(?) assign insn_device_config if we have global names for + * this device. + */ + dev->insn_device_config = ni_global_insn_config; + dev->get_valid_routes = _ni_get_valid_routes; + } if (board->n_aochan > MAX_N_AO_CHAN) { dev_err(dev->class_dev, "bug! n_aochan > MAX_N_AO_CHAN\n"); @@ -5508,7 +6224,9 @@ static int ni_E_init(struct comedi_device *dev, (devpriv->is_m_series) ? ni_gpct_variant_m_series : ni_gpct_variant_e_series, - NUM_GPCT); + NUM_GPCT, + NUM_GPCT, + &devpriv->routing_tables); if (!devpriv->counter_dev) return -ENOMEM; @@ -5517,8 +6235,6 @@ static int ni_E_init(struct comedi_device *dev, struct ni_gpct *gpct = &devpriv->counter_dev->counters[i]; /* setup and initialize the counter */ - gpct->chip_index = 0; - gpct->counter_index = i; ni_tio_init_counter(gpct); s = &dev->subdevices[NI_GPCT_SUBDEV(i)]; @@ -5544,6 +6260,10 @@ static int ni_E_init(struct comedi_device *dev, s->private = gpct; } + /* Initialize GPFO_{0,1} to produce output of counters */ + ni_set_gout_routing(0, 0, dev); /* output of counter 0; DAQ STC, p338 */ + ni_set_gout_routing(0, 1, dev); /* output of counter 1; DAQ STC, p338 */ + /* Frequency output subdevice */ s = &dev->subdevices[NI_FREQ_OUT_SUBDEV]; s->type = COMEDI_SUBD_COUNTER; diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c @@ -260,18 +260,22 @@ enum nidio_boardid { struct nidio_board { const char *name; unsigned int uses_firmware:1; + unsigned int dio_speed; }; static const struct nidio_board nidio_boards[] = { [BOARD_PCIDIO_32HS] = { .name = "pci-dio-32hs", + .dio_speed = 50, }, [BOARD_PXI6533] = { .name = "pxi-6533", + .dio_speed = 50, }, [BOARD_PCI6534] = { .name = "pci-6534", .uses_firmware = 1, + .dio_speed = 50, }, }; @@ -467,6 +471,15 @@ static int ni_pcidio_insn_config(struct comedi_device *dev, { int ret; + if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) { + const struct nidio_board *board = dev->board_ptr; + + /* we don't care about actual channels */ + data[1] = board->dio_speed; + data[2] = 0; + return 0; + } + ret = comedi_dio_insn_config(dev, s, insn, data, 0); if (ret) return ret; diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -693,6 +693,7 @@ static const struct ni_board_struct ni_boards[] = { .ai_speed = 4000, .reg_type = ni_reg_622x, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PCI6221] = { .name = "pci-6221", @@ -708,6 +709,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_622x, .ao_speed = 1200, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PCI6221_37PIN] = { .name = "pci-6221_37pin", @@ -738,6 +740,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_622x, .ao_speed = 1200, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PCI6224] = { .name = "pci-6224", @@ -749,6 +752,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_622x, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PXI6224] = { .name = "pxi-6224", @@ -760,6 +764,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_622x, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PCI6225] = { .name = "pci-6225", @@ -776,6 +781,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 1200, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PXI6225] = { .name = "pxi-6225", @@ -792,6 +798,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 1200, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PCI6229] = { .name = "pci-6229", @@ -824,6 +831,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 1200, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PCI6250] = { .name = "pci-6250", @@ -844,6 +852,7 @@ static const struct ni_board_struct ni_boards[] = { .ai_speed = 800, .reg_type = ni_reg_625x, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6251] = { .name = "pci-6251", @@ -859,6 +868,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_625x, .ao_speed = 350, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PXI6251] = { .name = "pxi-6251", @@ -874,6 +884,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_625x, .ao_speed = 350, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCIE6251] = { .name = "pcie-6251", @@ -889,6 +900,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_625x, .ao_speed = 350, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PXIE6251] = { .name = "pxie-6251", @@ -904,6 +916,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_625x, .ao_speed = 350, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6254] = { .name = "pci-6254", @@ -926,6 +939,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_625x, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6259] = { .name = "pci-6259", @@ -958,6 +972,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 350, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCIE6259] = { .name = "pcie-6259", @@ -990,6 +1005,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 350, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6280] = { .name = "pci-6280", @@ -1012,6 +1028,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_fifo_depth = 8191, .reg_type = ni_reg_628x, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6281] = { .name = "pci-6281", @@ -1027,6 +1044,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_628x, .ao_speed = 350, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PXI6281] = { .name = "pxi-6281", @@ -1042,6 +1060,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_628x, .ao_speed = 350, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6284] = { .name = "pci-6284", @@ -1064,6 +1083,7 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_628x, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6289] = { .name = "pci-6289", @@ -1096,6 +1116,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 350, .has_32dio_chan = 1, .caldac = { caldac_none }, + .dio_speed = 100, }, [BOARD_PCI6143] = { .name = "pci-6143", diff --git a/drivers/staging/comedi/drivers/ni_routes.c b/drivers/staging/comedi/drivers/ni_routes.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routes.c + * Route information for NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/bsearch.h> +#include <linux/sort.h> + +#include "../comedi.h" + +#include "ni_routes.h" +#include "ni_routing/ni_route_values.h" +#include "ni_routing/ni_device_routes.h" + +/* + * This is defined in ni_routing/ni_route_values.h: + * #define B(x) ((x) - NI_NAMES_BASE) + */ + +/* + * These are defined in ni_routing/ni_route_values.h to identify clearly + * elements of the table that were set. In other words, entries that are zero + * are invalid. To get the value to use for the register, one must mask out the + * high bit. + * + * #define V(x) ((x) | 0x80) + * + * #define UNMARK(x) ((x) & (~(0x80))) + * + */ + +/* Helper for accessing data. */ +#define RVi(table, src, dest) ((table)[(dest) * NI_NUM_NAMES + (src)]) + +static const size_t route_table_size = NI_NUM_NAMES * NI_NUM_NAMES; + +/* + * Find the proper route_values and ni_device_routes tables for this particular + * device. + * + * Return: -ENODATA if either was not found; 0 if both were found. + */ +static int ni_find_device_routes(const char *device_family, + const char *board_name, + struct ni_route_tables *tables) +{ + const struct ni_device_routes *dr = NULL; + const u8 *rv = NULL; + int i; + + /* First, find the register_values table for this device family */ + for (i = 0; ni_all_route_values[i]; ++i) { + if (memcmp(ni_all_route_values[i]->family, device_family, + strnlen(device_family, 30)) == 0) { + rv = &ni_all_route_values[i]->register_values[0][0]; + break; + } + } + + if (!rv) + return -ENODATA; + + /* Second, find the set of routes valid for this device. */ + for (i = 0; ni_device_routes_list[i]; ++i) { + if (memcmp(ni_device_routes_list[i]->device, board_name, + strnlen(board_name, 30)) == 0) { + dr = ni_device_routes_list[i]; + break; + } + } + + if (!dr) + return -ENODATA; + + tables->route_values = rv; + tables->valid_routes = dr; + + return 0; +} + +/** + * ni_assign_device_routes() - Assign the proper lookup table for NI signal + * routing to the specified NI device. + * + * Return: -ENODATA if assignment was not successful; 0 if successful. + */ +int ni_assign_device_routes(const char *device_family, + const char *board_name, + struct ni_route_tables *tables) +{ + memset(tables, 0, sizeof(struct ni_route_tables)); + return ni_find_device_routes(device_family, board_name, tables); +} +EXPORT_SYMBOL_GPL(ni_assign_device_routes); + +/** + * ni_count_valid_routes() - Count the number of valid routes. + * @tables: Routing tables for which to count all valid routes. + */ +unsigned int ni_count_valid_routes(const struct ni_route_tables *tables) +{ + int total = 0; + int i; + + for (i = 0; i < tables->valid_routes->n_route_sets; ++i) { + const struct ni_route_set *R = &tables->valid_routes->routes[i]; + int j; + + for (j = 0; j < R->n_src; ++j) { + const int src = R->src[j]; + const int dest = R->dest; + const u8 *rv = tables->route_values; + + if (RVi(rv, B(src), B(dest))) + /* direct routing is valid */ + ++total; + else if (channel_is_rtsi(dest) && + (RVi(rv, B(src), B(NI_RGOUT0)) || + RVi(rv, B(src), B(NI_RTSI_BRD(0))) || + RVi(rv, B(src), B(NI_RTSI_BRD(1))) || + RVi(rv, B(src), B(NI_RTSI_BRD(2))) || + RVi(rv, B(src), B(NI_RTSI_BRD(3))))) { + ++total; + } + } + } + return total; +} +EXPORT_SYMBOL_GPL(ni_count_valid_routes); + +/** + * ni_get_valid_routes() - Implements INSN_DEVICE_CONFIG_GET_ROUTES. + * @tables: pointer to relevant set of routing tables. + * @n_pairs: Number of pairs for which memory is allocated by the user. If + * the user specifies '0', only the number of available pairs is + * returned. + * @pair_data: Pointer to memory allocated to return pairs back to user. Each + * even, odd indexed member of this array will hold source, + * destination of a route pair respectively. + * + * Return: the number of valid routes if n_pairs == 0; otherwise, the number of + * valid routes copied. + */ +unsigned int ni_get_valid_routes(const struct ni_route_tables *tables, + unsigned int n_pairs, + unsigned int *pair_data) +{ + unsigned int n_valid = ni_count_valid_routes(tables); + int i; + + if (n_pairs == 0 || n_valid == 0) + return n_valid; + + if (!pair_data) + return 0; + + n_valid = 0; + + for (i = 0; i < tables->valid_routes->n_route_sets; ++i) { + const struct ni_route_set *R = &tables->valid_routes->routes[i]; + int j; + + for (j = 0; j < R->n_src; ++j) { + const int src = R->src[j]; + const int dest = R->dest; + bool valid = false; + const u8 *rv = tables->route_values; + + if (RVi(rv, B(src), B(dest))) + /* direct routing is valid */ + valid = true; + else if (channel_is_rtsi(dest) && + (RVi(rv, B(src), B(NI_RGOUT0)) || + RVi(rv, B(src), B(NI_RTSI_BRD(0))) || + RVi(rv, B(src), B(NI_RTSI_BRD(1))) || + RVi(rv, B(src), B(NI_RTSI_BRD(2))) || + RVi(rv, B(src), B(NI_RTSI_BRD(3))))) { + /* indirect routing also valid */ + valid = true; + } + + if (valid) { + pair_data[2 * n_valid] = src; + pair_data[2 * n_valid + 1] = dest; + ++n_valid; + } + + if (n_valid >= n_pairs) + return n_valid; + } + } + return n_valid; +} +EXPORT_SYMBOL_GPL(ni_get_valid_routes); + +/** + * List of NI global signal names that, as destinations, are only routeable + * indirectly through the *_arg elements of the comedi_cmd structure. + */ +static const int NI_CMD_DESTS[] = { + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + NI_DI_SampleClock, + NI_DO_SampleClock, +}; + +/** + * ni_is_cmd_dest() - Determine whether the given destination is only + * configurable via a comedi_cmd struct. + * @dest: Destination to test. + */ +bool ni_is_cmd_dest(int dest) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(NI_CMD_DESTS); ++i) + if (NI_CMD_DESTS[i] == dest) + return true; + return false; +} +EXPORT_SYMBOL_GPL(ni_is_cmd_dest); + +/* **** BEGIN Routes sort routines **** */ +static int _ni_sort_destcmp(const void *va, const void *vb) +{ + const struct ni_route_set *a = va; + const struct ni_route_set *b = vb; + + if (a->dest < b->dest) + return -1; + else if (a->dest > b->dest) + return 1; + return 0; +} + +static int _ni_sort_srccmp(const void *vsrc0, const void *vsrc1) +{ + const int *src0 = vsrc0; + const int *src1 = vsrc1; + + if (*src0 < *src1) + return -1; + else if (*src0 > *src1) + return 1; + return 0; +} + +/** + * ni_sort_device_routes() - Sort the list of valid device signal routes in + * preparation for use. + * @valid_routes: pointer to ni_device_routes struct to sort. + */ +void ni_sort_device_routes(struct ni_device_routes *valid_routes) +{ + unsigned int n; + + /* 1. Count and set the number of ni_route_set objects. */ + valid_routes->n_route_sets = 0; + while (valid_routes->routes[valid_routes->n_route_sets].dest != 0) + ++valid_routes->n_route_sets; + + /* 2. sort all ni_route_set objects by destination. */ + sort(valid_routes->routes, valid_routes->n_route_sets, + sizeof(struct ni_route_set), _ni_sort_destcmp, NULL); + + /* 3. Loop through each route_set for sorting. */ + for (n = 0; n < valid_routes->n_route_sets; ++n) { + struct ni_route_set *rs = &valid_routes->routes[n]; + + /* 3a. Count and set the number of sources. */ + rs->n_src = 0; + while (rs->src[rs->n_src]) + ++rs->n_src; + + /* 3a. Sort sources. */ + sort(valid_routes->routes[n].src, valid_routes->routes[n].n_src, + sizeof(int), _ni_sort_srccmp, NULL); + } +} +EXPORT_SYMBOL_GPL(ni_sort_device_routes); + +/* sort all valid device signal routes in prep for use */ +static void ni_sort_all_device_routes(void) +{ + unsigned int i; + + for (i = 0; ni_device_routes_list[i]; ++i) + ni_sort_device_routes(ni_device_routes_list[i]); +} + +/* **** BEGIN Routes search routines **** */ +static int _ni_bsearch_destcmp(const void *vkey, const void *velt) +{ + const int *key = vkey; + const struct ni_route_set *elt = velt; + + if (*key < elt->dest) + return -1; + else if (*key > elt->dest) + return 1; + return 0; +} + +static int _ni_bsearch_srccmp(const void *vkey, const void *velt) +{ + const int *key = vkey; + const int *elt = velt; + + if (*key < *elt) + return -1; + else if (*key > *elt) + return 1; + return 0; +} + +/** + * ni_find_route_set() - Finds the proper route set with the specified + * destination. + * @destination: Destination of which to search for the route set. + * @valid_routes: Pointer to device routes within which to search. + * + * Return: NULL if no route_set is found with the specified @destination; + * otherwise, a pointer to the route_set if found. + */ +const struct ni_route_set * +ni_find_route_set(const int destination, + const struct ni_device_routes *valid_routes) +{ + return bsearch(&destination, valid_routes->routes, + valid_routes->n_route_sets, sizeof(struct ni_route_set), + _ni_bsearch_destcmp); +} +EXPORT_SYMBOL_GPL(ni_find_route_set); + +/** + * ni_route_set_has_source() - Determines whether the given source is in + * included given route_set. + * + * Return: true if found; false otherwise. + */ +bool ni_route_set_has_source(const struct ni_route_set *routes, + const int source) +{ + if (!bsearch(&source, routes->src, routes->n_src, sizeof(int), + _ni_bsearch_srccmp)) + return false; + return true; +} +EXPORT_SYMBOL_GPL(ni_route_set_has_source); + +/** + * ni_lookup_route_register() - Look up a register value for a particular route + * without checking whether the route is valid for + * the particular device. + * @src: global-identifier for route source + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * + * Return: -EINVAL if the specified route is not valid for this device family. + */ +s8 ni_lookup_route_register(int src, int dest, + const struct ni_route_tables *tables) +{ + s8 regval; + + /* + * Be sure to use the B() macro to subtract off the NI_NAMES_BASE before + * indexing into the route_values array. + */ + src = B(src); + dest = B(dest); + if (src < 0 || src >= NI_NUM_NAMES || dest < 0 || dest >= NI_NUM_NAMES) + return -EINVAL; + regval = RVi(tables->route_values, src, dest); + if (!regval) + return -EINVAL; + /* mask out the valid-value marking bit */ + return UNMARK(regval); +} +EXPORT_SYMBOL_GPL(ni_lookup_route_register); + +/** + * ni_route_to_register() - Validates and converts the specified signal route + * (src-->dest) to the value used at the appropriate + * register. + * @src: global-identifier for route source + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * + * Generally speaking, most routes require the first six bits and a few require + * 7 bits. Special handling is given for the return value when the route is to + * be handled by the RTSI sub-device. In this case, the returned register may + * not be sufficient to define the entire route path, but rather may only + * indicate the intermediate route. For example, if the route must go through + * the RGOUT0 pin, the (src->RGOUT0) register value will be returned. + * Similarly, if the route must go through the NI_RTSI_BRD lines, the BIT(6) + * will be set: + * + * if route does not need RTSI_BRD lines: + * bits 0:7 : register value + * for a route that must go through RGOUT0 pin, this will be equal + * to the (src->RGOUT0) register value. + * else: * route is (src->RTSI_BRD(x), RTSI_BRD(x)->TRIGGER_LINE(i)) * + * bits 0:5 : zero + * bits 6 : set to 1 + * bits 7:7 : zero + * + * Return: register value to be used for source at destination with special + * cases given above; Otherwise, -1 if the specified route is not valid for + * this particular device. + */ +s8 ni_route_to_register(const int src, const int dest, + const struct ni_route_tables *tables) +{ + const struct ni_route_set *routes = + ni_find_route_set(dest, tables->valid_routes); + const u8 *rv; + s8 regval; + + /* first check to see if source is listed with bunch of destinations. */ + if (!routes) + return -1; + /* 2nd, check to see if destination is in list of source's targets. */ + if (!ni_route_set_has_source(routes, src)) + return -1; + /* + * finally, check to see if we know how to route... + * Be sure to use the B() macro to subtract off the NI_NAMES_BASE before + * indexing into the route_values array. + */ + rv = tables->route_values; + regval = RVi(rv, B(src), B(dest)); + + /* + * if we did not validate the route, we'll see if we can route through + * one of the muxes + */ + if (!regval && channel_is_rtsi(dest)) { + regval = RVi(rv, B(src), B(NI_RGOUT0)); + if (!regval && (RVi(rv, B(src), B(NI_RTSI_BRD(0))) || + RVi(rv, B(src), B(NI_RTSI_BRD(1))) || + RVi(rv, B(src), B(NI_RTSI_BRD(2))) || + RVi(rv, B(src), B(NI_RTSI_BRD(3))))) + regval = BIT(6); + } + + if (!regval) + return -1; + /* mask out the valid-value marking bit */ + return UNMARK(regval); +} +EXPORT_SYMBOL_GPL(ni_route_to_register); + +/** + * ni_find_route_source() - Finds the signal source corresponding to a signal + * route (src-->dest) of the specified routing register + * value and the specified route destination on the + * specified device. + * + * Note that this function does _not_ validate the source based on device + * routes. + * + * Return: The NI signal value (e.g. NI_PFI(0) or PXI_Clk10) if found. + * If the source was not found (i.e. the register value is not + * valid for any routes to the destination), -EINVAL is returned. + */ +int ni_find_route_source(const u8 src_sel_reg_value, int dest, + const struct ni_route_tables *tables) +{ + int src; + + dest = B(dest); /* subtract NI names offset */ + /* ensure we are not going to under/over run the route value table */ + if (dest < 0 || dest >= NI_NUM_NAMES) + return -EINVAL; + for (src = 0; src < NI_NUM_NAMES; ++src) + if (RVi(tables->route_values, src, dest) == + V(src_sel_reg_value)) + return src + NI_NAMES_BASE; + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ni_find_route_source); + +/* **** END Routes search routines **** */ + +/* **** BEGIN simple module entry/exit functions **** */ +static int __init ni_routes_module_init(void) +{ + ni_sort_all_device_routes(); + return 0; +} + +static void __exit ni_routes_module_exit(void) +{ +} + +module_init(ni_routes_module_init); +module_exit(ni_routes_module_exit); + +MODULE_AUTHOR("Comedi http://www.comedi.org"); +MODULE_DESCRIPTION("Comedi helper for routing signals-->terminals for NI"); +MODULE_LICENSE("GPL"); +/* **** END simple module entry/exit functions **** */ diff --git a/drivers/staging/comedi/drivers/ni_routes.h b/drivers/staging/comedi/drivers/ni_routes.h @@ -0,0 +1,329 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routes.h + * Route information for NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _COMEDI_DRIVERS_NI_ROUTES_H +#define _COMEDI_DRIVERS_NI_ROUTES_H + +#include <linux/types.h> +#include <linux/errno.h> + +#ifndef NI_ROUTE_VALUE_EXTERNAL_CONVERSION +#include <linux/bitops.h> +#endif + +#include "../comedi.h" + +/** + * struct ni_route_set - Set of destinations with a common source. + * @dest: Destination of all sources in this route set. + * @n_src: Number of sources for this route set. + * @src: List of sources that all map to the same destination. + */ +struct ni_route_set { + int dest; + int n_src; + int *src; +}; + +/** + * struct ni_device_routes - List of all src->dest sets for a particular device. + * @device: Name of board/device (e.g. pxi-6733). + * @n_route_sets: Number of route sets that are valid for this device. + * @routes: List of route sets that are valid for this device. + */ +struct ni_device_routes { + const char *device; + int n_route_sets; + struct ni_route_set *routes; +}; + +/** + * struct ni_route_tables - Register values and valid routes for a device. + * @valid_routes: Pointer to a all valid route sets for a single device. + * @route_values: Pointer to register values for all routes for the family to + * which the device belongs. + * + * Link to the valid src->dest routes and the register values used to assign + * such routes for that particular device. + */ +struct ni_route_tables { + const struct ni_device_routes *valid_routes; + const u8 *route_values; +}; + +/* + * ni_assign_device_routes() - Assign the proper lookup table for NI signal + * routing to the specified NI device. + * + * Return: -ENODATA if assignment was not successful; 0 if successful. + */ +int ni_assign_device_routes(const char *device_family, + const char *board_name, + struct ni_route_tables *tables); + +/* + * ni_find_route_set() - Finds the proper route set with the specified + * destination. + * @destination: Destination of which to search for the route set. + * @valid_routes: Pointer to device routes within which to search. + * + * Return: NULL if no route_set is found with the specified @destination; + * otherwise, a pointer to the route_set if found. + */ +const struct ni_route_set * +ni_find_route_set(const int destination, + const struct ni_device_routes *valid_routes); + +/* + * ni_route_set_has_source() - Determines whether the given source is in + * included given route_set. + * + * Return: true if found; false otherwise. + */ +bool ni_route_set_has_source(const struct ni_route_set *routes, const int src); + +/* + * ni_route_to_register() - Validates and converts the specified signal route + * (src-->dest) to the value used at the appropriate + * register. + * @src: global-identifier for route source + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * + * Generally speaking, most routes require the first six bits and a few require + * 7 bits. Special handling is given for the return value when the route is to + * be handled by the RTSI sub-device. In this case, the returned register may + * not be sufficient to define the entire route path, but rather may only + * indicate the intermediate route. For example, if the route must go through + * the RGOUT0 pin, the (src->RGOUT0) register value will be returned. + * Similarly, if the route must go through the NI_RTSI_BRD lines, the BIT(6) + * will be set: + * + * if route does not need RTSI_BRD lines: + * bits 0:7 : register value + * for a route that must go through RGOUT0 pin, this will be equal + * to the (src->RGOUT0) register value. + * else: * route is (src->RTSI_BRD(x), RTSI_BRD(x)->TRIGGER_LINE(i)) * + * bits 0:5 : zero + * bits 6 : set to 1 + * bits 7:7 : zero + * + * Return: register value to be used for source at destination with special + * cases given above; Otherwise, -1 if the specified route is not valid for + * this particular device. + */ +s8 ni_route_to_register(const int src, const int dest, + const struct ni_route_tables *tables); + +static inline bool ni_rtsi_route_requires_mux(s8 value) +{ + return value & BIT(6); +} + +/* + * ni_lookup_route_register() - Look up a register value for a particular route + * without checking whether the route is valid for + * the particular device. + * @src: global-identifier for route source + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * + * Return: -EINVAL if the specified route is not valid for this device family. + */ +s8 ni_lookup_route_register(int src, int dest, + const struct ni_route_tables *tables); + +/** + * route_is_valid() - Determines whether the specified signal route (src-->dest) + * is valid for the given NI comedi_device. + * @src: global-identifier for route source + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * + * Return: True if the route is valid, otherwise false. + */ +static inline bool route_is_valid(const int src, const int dest, + const struct ni_route_tables *tables) +{ + return ni_route_to_register(src, dest, tables) >= 0; +} + +/* + * ni_is_cmd_dest() - Determine whether the given destination is only + * configurable via a comedi_cmd struct. + * @dest: Destination to test. + */ +bool ni_is_cmd_dest(int dest); + +static inline bool channel_is_pfi(int channel) +{ + return NI_PFI(0) <= channel && channel <= NI_PFI(-1); +} + +static inline bool channel_is_rtsi(int channel) +{ + return TRIGGER_LINE(0) <= channel && channel <= TRIGGER_LINE(-1); +} + +static inline bool channel_is_ctr(int channel) +{ + return channel >= NI_COUNTER_NAMES_BASE && + channel <= NI_COUNTER_NAMES_MAX; +} + +/* + * ni_count_valid_routes() - Count the number of valid routes. + * @tables: Routing tables for which to count all valid routes. + */ +unsigned int ni_count_valid_routes(const struct ni_route_tables *tables); + +/* + * ni_get_valid_routes() - Implements INSN_DEVICE_CONFIG_GET_ROUTES. + * @tables: pointer to relevant set of routing tables. + * @n_pairs: Number of pairs for which memory is allocated by the user. If + * the user specifies '0', only the number of available pairs is + * returned. + * @pair_data: Pointer to memory allocated to return pairs back to user. Each + * even, odd indexed member of this array will hold source, + * destination of a route pair respectively. + * + * Return: the number of valid routes if n_pairs == 0; otherwise, the number of + * valid routes copied. + */ +unsigned int ni_get_valid_routes(const struct ni_route_tables *tables, + unsigned int n_pairs, + unsigned int *pair_data); + +/* + * ni_sort_device_routes() - Sort the list of valid device signal routes in + * preparation for use. + * @valid_routes: pointer to ni_device_routes struct to sort. + */ +void ni_sort_device_routes(struct ni_device_routes *valid_routes); + +/* + * ni_find_route_source() - Finds the signal source corresponding to a signal + * route (src-->dest) of the specified routing register + * value and the specified route destination on the + * specified device. + * + * Note that this function does _not_ validate the source based on device + * routes. + * + * Return: The NI signal value (e.g. NI_PFI(0) or PXI_Clk10) if found. + * If the source was not found (i.e. the register value is not + * valid for any routes to the destination), -EINVAL is returned. + */ +int ni_find_route_source(const u8 src_sel_reg_value, const int dest, + const struct ni_route_tables *tables); + +/** + * route_register_is_valid() - Determines whether the register value for the + * specified route destination on the specified + * device is valid. + */ +static inline bool route_register_is_valid(const u8 src_sel_reg_value, + const int dest, + const struct ni_route_tables *tables) +{ + return ni_find_route_source(src_sel_reg_value, dest, tables) >= 0; +} + +/** + * ni_get_reg_value_roffs() - Determines the proper register value for a + * particular valid NI signal/terminal route. + * @src: Either a direct register value or one of NI_* signal names. + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * @direct_reg_offset: + * Compatibility compensation argument. This argument allows us to + * arbitrarily apply an offset to src if src is a direct register + * value reference. This is necessary to be compatible with + * definitions of register values as previously exported directly + * to user space. + * + * Return: the register value (>0) to be used at the destination if the src is + * valid for the given destination; -1 otherwise. + */ +static inline s8 ni_get_reg_value_roffs(int src, const int dest, + const struct ni_route_tables *tables, + const int direct_reg_offset) +{ + if (src < NI_NAMES_BASE) { + src += direct_reg_offset; + /* + * In this case, the src is expected to actually be a register + * value. + */ + if (route_register_is_valid(src, dest, tables)) + return src; + return -1; + } + + /* + * Otherwise, the src is expected to be one of the abstracted NI + * signal/terminal names. + */ + return ni_route_to_register(src, dest, tables); +} + +static inline int ni_get_reg_value(const int src, const int dest, + const struct ni_route_tables *tables) +{ + return ni_get_reg_value_roffs(src, dest, tables, 0); +} + +/** + * ni_check_trigger_arg_roffs() - Checks the trigger argument (*_arg) of an NI + * device to ensure that the *_arg value + * corresponds to _either_ a valid register value + * to define a trigger source, _or_ a valid NI + * signal/terminal name that has a valid route to + * the destination on the particular device. + * @src: Either a direct register value or one of NI_* signal names. + * @dest: global-identifier for route destination + * @tables: pointer to relevant set of routing tables. + * @direct_reg_offset: + * Compatibility compensation argument. This argument allows us to + * arbitrarily apply an offset to src if src is a direct register + * value reference. This is necessary to be compatible with + * definitions of register values as previously exported directly + * to user space. + * + * Return: 0 if the src (either register value or NI signal/terminal name) is + * valid for the destination; -EINVAL otherwise. + */ +static inline +int ni_check_trigger_arg_roffs(int src, const int dest, + const struct ni_route_tables *tables, + const int direct_reg_offset) +{ + if (ni_get_reg_value_roffs(src, dest, tables, direct_reg_offset) < 0) + return -EINVAL; + return 0; +} + +static inline int ni_check_trigger_arg(const int src, const int dest, + const struct ni_route_tables *tables) +{ + return ni_check_trigger_arg_roffs(src, dest, tables, 0); +} + +#endif /* _COMEDI_DRIVERS_NI_ROUTES_H */ diff --git a/drivers/staging/comedi/drivers/ni_routing/README b/drivers/staging/comedi/drivers/ni_routing/README @@ -0,0 +1,240 @@ +Framework for Maintaining Common National Instruments Terminal/Signal names + +The contents of this directory are primarily for maintaining and formatting all +known valid signal routes for various National Instruments devices. + +Some background: + There have been significant confusions over the past many years for users + when trying to understand how to connect to/from signals and terminals on + NI hardware using comedi. The major reason for this is that the actual + register values were exposed and required to be used by users. Several + major reasons exist why this caused major confusion for users: + + 1) The register values are _NOT_ in user documentation, but rather in + arcane locations, such as a few register programming manuals that are + increasingly hard to find and the NI-MHDDK (comments in in example code). + There is no one place to find the various valid values of the registers. + + 2) The register values are _NOT_ completely consistent. There is no way to + gain any sense of intuition of which values, or even enums one should use + for various registers. There was some attempt in prior use of comedi to + name enums such that a user might know which enums should be used for + varying purposes, but the end-user had to gain a knowledge of register + values to correctly wield this approach. + + 3) The names for signals and registers found in the various register level + programming manuals and vendor-provided documentation are _not_ even + close to the same names that are in the end-user documentation. + + 4) The sets of routes that are valid are not consistent from device to device. + One additional major challenge is that this information does not seem to be + obtainable in any programmatic fashion, neither through the proprietary + NIDAQmx(-base) c-libraries, nor with register level programming, _nor_ + through any documentation. In fact, the only consistent source of this + information is through the proprietary NI-MAX software, which currently only + runs on Windows platforms. A further challenge is that this information + cannot be exported from NI-MAX, except by screenshot. + + + +The content of this directory is part of an effort to greatly simplify the use +of signal routing capabilities of National Instruments data-acquisition and +control hardware. In order to facilitate the transfer of register-level +information _and_ the knowledge of valid routes per device, a few specific +choices were made: + + +1) The names of the National Instruments signals/terminals that are used in this + directory are chosen to be consistent with (a) the NI's user level + documentation, (b) NI's user-level code, (c) the information as provided by + the proprietary NI-MAX software, and (d) the user interface code provided by + the user-land comedilib library. + + The impact of this choice implies that one allows the use of CamelScript names + in the kernel. In short, the choice to use CamelScript and the exact names + below is for maintainability, clarity, similarity to manufacturer's + documentation, _and_ a mitigation for confusion that has plagued the use of + these drivers for years! + +2) The bulk of the real content for this directory is stored in two separate + collections (i.e. sub-directories) of tables stored in c source files: + + (a) ni_route_values/ni_[series-label]series.c + + This data represents all the various register values to use for the + multiple different signal MUXes for the specific device families. + + The values are all wrapped in one of three macros to help document and + track which values have been implemented and tested. + These macros are: + V(<value>) : register value is valid, tested, and implemented + I(<value>) : register value is implemented but needs testing + U(<value>) : register value is not implemented + + The actual function of these macros will depend on whether the code is + compiled in the kernel or whether it is compiled into the conversion + tools. For the conversion tools, it can be used to indicate the status + of the register value. For the kernel, V() and I() both perform the + same function and prepare data to be used; U() zeroes out the value to + ensure that it cannot be used. + + *** It would be a great help for users to test these values such that + these files can be correctly marked/documented *** + + (b) ni_device_routes/[board-name].c + + This data represents the known set of valid signal routes that are + possible for each specific board. Although the family defines the + register values to use for a particular signal MUX, not all possible + signals are actually available on each board. + + In order for a particular board to take advantage of the effort to + simplify/clarify signal routing on NI devices, a corresponding + [board-name].c file must be created. This file should reflect the known + valid _direct_ routing capabilities of the board. + + As noted above, the only known consistent source of information for + valid device routes comes from the proprietary National Instruments + Windows software, NI-MAX. Also, as noted above, this information can + only be visually conveyed from NI-MAX to other media. To make this + easier, the naming conventions used in the [board-name].c file are + similar to the naming conventions as presented by NI-MAX. + + +3) Two other files aggregate the above data to integrate it into comedi: + ni_route_values.c + ni_device_routes.c + + When adding a new [board-name].c file, be sure to also add in the line in + ni_device_routes.c to include this information into comedi. + + +4) Several tools have been included to convert from/to the c file formats. + These tools are best used/demonstrated via the included Makefile targets: + (a) `make csv-files` + Creates new csv-files using content of c-files of existing + ni_routing/* content. New csv files are placed in csv + sub-directory. + + As noted above, the only consistent source of information of valid + device routes comes from the proprietary National Instruments Windows + software, NI-MAX. Also, as noted above, this information can only be + visually conveyed from NI-MAX to other media. This make target creates + spreadsheet representations of the routing data. The choice of using a + spreadsheet (ala CSV) to copy this information allows for easy direct + visual comparison to the NI-MAX "Valid Routes" tables. + + Furthermore, the register-level information is much easier to identify and + correct when entire families of NI devices are shown side by side in table + format. This is made easy by using a file-storage format that can be + loaded into a spreadsheet application. + + Finally, .csv content is very easy to edit and read using a variety of + tools, including spreadsheets or various other scripting languages. In + fact, the tools provided here enable quick conversion of the + spreadsheet-like .csv format to c-files that follow the kernel coding + conventions. + + + (b) `make c-files` + Creates new c-files using content of csv sub-directory. These + new c-files can be compared to the active content in the + ni_routing directory. + (c) `make csv-blank` + Create a new blank csv file. This is useful for establishing a + new data table for either a device family (less likely) or a + specific board of an existing device family (more likely). + (d) `make clean` + Remove all generated files/directories. + (e) `make everything` + Build all csv-files, then all new c-files. + + + + +In summary, similar confusion about signal routing configuration, albeit less, +plagued NI's previous version of their own proprietary drivers. Earlier than +2003, NI greatly simplified the situation for users by releasing a new API that +abstracted the names of signals/terminals to a common and intuitive set of +names. In addition, this new API provided a much more common interface to use +for most of NI hardware. + +Comedi already provides such a common interface for data-acquisition and control +hardware. This effort complements comedi's abstraction layers by further +abstracting much more of the use cases for NI hardware, but allowing users _and_ +developers to directly refer to NI documentation (user-level, register-level, +and the register-level examples of the NI-MHDDK). + + + +-------------------------------------------------------------------------------- +Various naming conventions and relations: +-------------------------------------------------------------------------------- +These are various notes that help to relate the naming conventions used in the +NI-STC with those naming conventions used here. +-------------------------------------------------------------------------------- + + Signal sources for most signals-destinations are given a specific naming + convention, although the register values are not consistent. This next table + shows the mapping between the names used in comedi for NI and those names + typically used within the NI-STC documentation. + + (comedi) (NI-STC input or output) (NOTE) + ------------------------------------------------------------------------------ + TRIGGER_LINE(i) RTSI_Trig_i_Output_Select i in range [0..7] + NI_AI_STOP AI_STOP + NI_AI_SampleClock AI_START_Select + NI_AI_SampleClockTimebase AI_SI If internal sample + clock signal is used + NI_AI_StartTrigger AI_START1_Select + NI_AI_ReferenceTrigger AI_START2_Select for pre-triggered + acquisition---not + currently supported + in comedi + NI_AI_ConvertClock AI_CONVERT_Source_Select + NI_AI_ConvertClockTimebase AI_SI2 If internal convert + signal is used + NI_AI_HoldCompleteEvent + NI_AI_PauseTrigger AI_External_Gate + NI_AO_SampleClock AO_UPDATE + NI_AO_SampleClockTimebase AO_UI + NI_AO_StartTrigger AO_START1 + NI_AO_PauseTrigger AO_External_Gate + NI_DI_SampleClock + NI_DO_SampleClock + NI_MasterTimebase + NI_20MHzTimebase TIMEBASE 1 && TIMEBASE 3 if no higher clock exists + NI_80MHzTimebase TIMEBASE 3 + NI_100kHzTimebase TIMEBASE 2 + NI_10MHzRefClock + PXI_Clk10 + NI_CtrOut(0) GPFO_0 external ctr0out pin + NI_CtrOut(1) GPFO_1 external ctr1out pin + NI_CtrSource(0) + NI_CtrSource(1) + NI_CtrGate(0) + NI_CtrGate(1) + NI_CtrInternalOutput(0) G_OUT0, G0_TC for Ctr1Source, Ctr1Gate + NI_CtrInternalOutput(1) G_OUT1, G1_TC for Ctr0Source, Ctr0Gate + NI_RGOUT0 RGOUT0 internal signal + NI_FrequencyOutput + #NI_FrequencyOutputTimebase + NI_ChangeDetectionEvent + NI_RTSI_BRD(0) + NI_RTSI_BRD(1) + NI_RTSI_BRD(2) + NI_RTSI_BRD(3) + #NI_SoftwareStrobe + NI_LogicLow + NI_CtrA(0) G0_A_Select see M-Series user + manual (371022K-01) + NI_CtrA(1) G1_A_Select see M-Series user + manual (371022K-01) + NI_CtrB(0) G0_B_Select, up/down see M-Series user + manual (371022K-01) + NI_CtrB(1) G1_B_Select, up/down see M-Series user + manual (371022K-01) + NI_CtrZ(0) see M-Series user + manual (371022K-01) + NI_CtrZ(1) see M-Series user + manual (371022K-01) diff --git a/drivers/staging/comedi/drivers/ni_routing/ni_device_routes.c b/drivers/staging/comedi/drivers/ni_routing/ni_device_routes.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routing/ni_device_routes.c + * List of valid routes for specific NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The contents of this file are generated using the tools in + * comedi/drivers/ni_routing/tools + * + * Please use those tools to help maintain the contents of this file. + */ + +#include "ni_device_routes.h" +#include "ni_device_routes/all.h" + +struct ni_device_routes *const ni_device_routes_list[] = { + &ni_pxi_6030e_device_routes, + &ni_pci_6070e_device_routes, + &ni_pci_6220_device_routes, + &ni_pci_6221_device_routes, + &ni_pxi_6224_device_routes, + &ni_pxi_6225_device_routes, + &ni_pci_6229_device_routes, + &ni_pci_6251_device_routes, + &ni_pxi_6251_device_routes, + &ni_pxie_6251_device_routes, + &ni_pci_6254_device_routes, + &ni_pci_6259_device_routes, + &ni_pci_6534_device_routes, + &ni_pci_6602_device_routes, + &ni_pci_6713_device_routes, + &ni_pci_6723_device_routes, + &ni_pci_6733_device_routes, + &ni_pxi_6733_device_routes, + NULL, +}; diff --git a/drivers/staging/comedi/drivers/ni_routing/ni_device_routes.h b/drivers/staging/comedi/drivers/ni_routing/ni_device_routes.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routing/ni_device_routes.c + * List of valid routes for specific NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This file is meant to be included by comedi/drivers/ni_routes.c + */ + +#ifndef _COMEDI_DRIVERS_NI_ROUTINT_NI_DEVICE_ROUTES_H +#define _COMEDI_DRIVERS_NI_ROUTINT_NI_DEVICE_ROUTES_H + +#include "../ni_routes.h" + +extern struct ni_device_routes *const ni_device_routes_list[]; + +#endif /* _COMEDI_DRIVERS_NI_ROUTINT_NI_DEVICE_ROUTES_H */ diff --git a/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/all.h b/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/all.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routing/ni_device_routes/all.h + * List of valid routes for specific NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The contents of this file are generated using the tools in + * comedi/drivers/ni_routing/tools + * + * Please use those tools to help maintain the contents of this file. + */ + +#ifndef _COMEDI_DRIVERS_NI_ROUTING_NI_DEVICE_ROUTES_EXTERN_H +#define _COMEDI_DRIVERS_NI_ROUTING_NI_DEVICE_ROUTES_EXTERN_H + +#include "../ni_device_routes.h" + +extern struct ni_device_routes ni_pxi_6030e_device_routes; +extern struct ni_device_routes ni_pci_6070e_device_routes; +extern struct ni_device_routes ni_pci_6220_device_routes; +extern struct ni_device_routes ni_pci_6221_device_routes; +extern struct ni_device_routes ni_pxi_6224_device_routes; +extern struct ni_device_routes ni_pxi_6225_device_routes; +extern struct ni_device_routes ni_pci_6229_device_routes; +extern struct ni_device_routes ni_pci_6251_device_routes; +extern struct ni_device_routes ni_pxi_6251_device_routes; +extern struct ni_device_routes ni_pxie_6251_device_routes; +extern struct ni_device_routes ni_pci_6254_device_routes; +extern struct ni_device_routes ni_pci_6259_device_routes; +extern struct ni_device_routes ni_pci_6534_device_routes; +extern struct ni_device_routes ni_pxie_6535_device_routes; +extern struct ni_device_routes ni_pci_6602_device_routes; +extern struct ni_device_routes ni_pci_6713_device_routes; +extern struct ni_device_routes ni_pci_6723_device_routes; +extern struct ni_device_routes ni_pci_6733_device_routes; +extern struct ni_device_routes ni_pxi_6733_device_routes; +extern struct ni_device_routes ni_pxie_6738_device_routes; + +#endif //_COMEDI_DRIVERS_NI_ROUTING_NI_DEVICE_ROUTES_EXTERN_H diff --git a/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c b/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c + * List of valid routes for specific NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The contents of this file are generated using the tools in + * comedi/drivers/ni_routing/tools + * + * Please use those tools to help maintain the contents of this file. + */ + +#include "../ni_device_routes.h" +#include "all.h" + +struct ni_device_routes ni_pci_6070e_device_routes = { + .device = "pci-6070e", + .routes = (struct ni_route_set[]){ + { + .dest = NI_PFI(0), + .src = (int[]){ + NI_AI_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(1), + .src = (int[]){ + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(2), + .src = (int[]){ + NI_AI_ConvertClock, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(3), + .src = (int[]){ + NI_CtrSource(1), + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(4), + .src = (int[]){ + NI_CtrGate(1), + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(5), + .src = (int[]){ + NI_AO_SampleClock, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(6), + .src = (int[]){ + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(7), + .src = (int[]){ + NI_AI_SampleClock, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(8), + .src = (int[]){ + NI_CtrSource(0), + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(9), + .src = (int[]){ + NI_CtrGate(0), + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(0), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(1), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(2), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(3), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(4), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(5), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(6), + .src = (int[]){ + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrInternalOutput(0), + NI_CtrOut(0), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AO_SampleClock, + NI_AO_StartTrigger, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(7), + .src = (int[]){ + NI_20MHzTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrSource(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_MasterTimebase, + NI_20MHzTimebase, + NI_100kHzTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrSource(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_MasterTimebase, + NI_20MHzTimebase, + NI_100kHzTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrGate(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(1), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrGate(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(0), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrOut(0), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(0), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrOut(1), + .src = (int[]){ + NI_CtrInternalOutput(1), + 0, /* Termination */ + } + }, + { + .dest = NI_AI_SampleClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(0), + NI_AI_SampleClockTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_SampleClockTimebase, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_MasterTimebase, + NI_20MHzTimebase, + NI_100kHzTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_StartTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(0), + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_ReferenceTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_ConvertClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(0), + NI_AI_ConvertClockTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_ConvertClockTimebase, + .src = (int[]){ + TRIGGER_LINE(7), + NI_AI_SampleClockTimebase, + NI_MasterTimebase, + NI_20MHzTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_PauseTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_HoldComplete, + .src = (int[]){ + NI_AI_HoldCompleteEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AO_SampleClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_CtrInternalOutput(1), + NI_AO_SampleClockTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AO_SampleClockTimebase, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_MasterTimebase, + NI_20MHzTimebase, + NI_100kHzTimebase, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AO_StartTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_AI_StartTrigger, + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_AO_PauseTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + NI_AnalogComparisonEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_MasterTimebase, + .src = (int[]){ + TRIGGER_LINE(7), + NI_20MHzTimebase, + 0, /* Termination */ + } + }, + { /* Termination of list */ + .dest = 0, + }, + }, +}; diff --git a/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c b/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c @@ -0,0 +1,1418 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routing/ni_device_routes/pci-6220.c + * List of valid routes for specific NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The contents of this file are generated using the tools in + * comedi/drivers/ni_routing/tools + * + * Please use those tools to help maintain the contents of this file. + */ + +#include "../ni_device_routes.h" +#include "all.h" + +struct ni_device_routes ni_pci_6220_device_routes = { + .device = "pci-6220", + .routes = (struct ni_route_set[]){ + { + .dest = NI_PFI(0), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(1), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(2), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(3), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(4), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(5), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(6), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(7), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(8), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(9), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(10), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(11), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(12), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(13), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(14), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_PFI(15), + .src = (int[]){ + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_DI_SampleClock, + NI_DO_SampleClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(2), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(3), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(4), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(5), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(6), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = TRIGGER_LINE(7), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_CtrSource(0), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_PauseTrigger, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrSource(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrGate(1), + NI_20MHzTimebase, + NI_80MHzTimebase, + NI_100kHzTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrSource(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrGate(0), + NI_20MHzTimebase, + NI_80MHzTimebase, + NI_100kHzTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrGate(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(1), + NI_CtrInternalOutput(1), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrGate(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrInternalOutput(0), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrAux(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(1), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(1), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrAux(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrSource(0), + NI_CtrGate(0), + NI_CtrGate(1), + NI_CtrInternalOutput(0), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrA(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrA(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrB(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrB(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrZ(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrZ(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_CtrArmStartTrigger(0), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(1), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_CtrArmStartTrigger(1), + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(0), + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_SampleClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClockTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_SampleClockTimebase, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_20MHzTimebase, + NI_100kHzTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_StartTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + 0, /* Termination */ + } + }, + { + .dest = NI_AI_ReferenceTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_AI_ConvertClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_ConvertClockTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_ConvertClockTimebase, + .src = (int[]){ + NI_AI_SampleClockTimebase, + NI_20MHzTimebase, + 0, /* Termination */ + } + }, + { + .dest = NI_AI_PauseTrigger, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + 0, /* Termination */ + } + }, + { + .dest = NI_DI_SampleClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_ConvertClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { + .dest = NI_DO_SampleClock, + .src = (int[]){ + NI_PFI(0), + NI_PFI(1), + NI_PFI(2), + NI_PFI(3), + NI_PFI(4), + NI_PFI(5), + NI_PFI(6), + NI_PFI(7), + NI_PFI(8), + NI_PFI(9), + NI_PFI(10), + NI_PFI(11), + NI_PFI(12), + NI_PFI(13), + NI_PFI(14), + NI_PFI(15), + TRIGGER_LINE(0), + TRIGGER_LINE(1), + TRIGGER_LINE(2), + TRIGGER_LINE(3), + TRIGGER_LINE(4), + TRIGGER_LINE(5), + TRIGGER_LINE(6), + TRIGGER_LINE(7), + NI_CtrInternalOutput(0), + NI_CtrInternalOutput(1), + NI_AI_SampleClock, + NI_AI_ConvertClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + 0, /* Termination */ + } + }, + { /* Termination of list */ + .dest = 0, + }, + }, +}; diff --git a/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c b/drivers/staging/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c @@ -0,0 +1,1602 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ +/* + * comedi/drivers/ni_routing/ni_device_routes/pci-6221.c + * List of valid routes for specific NI boards. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The contents of this file are generated using the tools in + * comedi/drivers/ni_routing/tools + * + * Please use those tools to help maintain the contents of this file. + */ + +#include "../ni_device_routes.h" +#include "all.h" + +struct ni_device_routes ni_pci_6221_device_routes = { + .device = "pci-6221", + .routes = (struct ni_route_set[]){ + { + .dest = NI_PFI(0), + .src = (int[]){ + TRIGGER_LINE(0),