whiterose

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

commit e266ca36da7de45b64b05698e98e04b578a88888
parent e0f0ae838a25464179d37f355d763f9ec139fc15
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Wed,  6 Mar 2019 16:29:27 -0800

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

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

  Lots of good IIO driver updates and cleanups in here as always.
  Combined with the removal of the xgifb driver, we have a net "loss" of
  over 9000 lines in the pull request, always a nice thing.

  As the outreachy application process is currently happening, there are
  loads of tiny checkpatch cleanup fixes all over the staging tree,
  which accounts for the majority of the fixups"

* tag 'staging-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (341 commits)
  staging: mt7621-dma: remove license boilerplate text
  staging: mt7621-dma: add SPDX GPL-2.0+ license identifier
  Staging: ks7010: Replace typecast to int
  Staging: vt6655: Align a static function declaration
  staging: speakup: fix line over 80 characters.
  staging: mt7621-eth: Remove license boilerplate text
  staging: mt7621-eth: Add SPDX license identifier
  staging: ks7010: removed custom Michael MIC implementation.
  staging: rtl8192e: Fix space and suspect issue
  Staging: vt6655: Modify comment style of SPDX License Identifier
  Staging: vt6655: Modify comment style for SPDX-License-Identifier
  Staging: vt6655: Align a function declaration
  Staging: vt6655: Alignment of function declaration
  staging: rtl8712: Fix indentation issue
  staging: wilc1000: fix incorrent type in initializer
  staging: rtl8188eu: remove unused P2P_PRIVATE_IOCTL_SET_LEN
  staging: rtl8188eu: remove unused enum P2P_PROTO_WK_ID
  staging: rtl8723bs: Remove duplicated include from drv_types.h
  Staging: vt6655: Alignment should match open parenthesis
  staging: erofs: fix mis-acted TAIL merging behavior
  ...

Diffstat:
MDocumentation/ABI/testing/sysfs-bus-iio | 22++++++++++++++++++++--
ADocumentation/ABI/testing/sysfs-bus-iio-sps30 | 28++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/accel/mma8452.txt | 4++++
ADocumentation/devicetree/bindings/iio/adc/adi,ad7606.txt | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt | 41+++++++++++++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt | 4++++
ADocumentation/devicetree/bindings/iio/adc/ingenic,adc.txt | 48++++++++++++++++++++++++++++++++++++++++++++++++
RDocumentation/devicetree/bindings/staging/iio/adc/lpc32xx-adc.txt -> Documentation/devicetree/bindings/iio/adc/lpc32xx-adc.txt | 0
ADocumentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt | 24++++++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/adc/samsung,exynos-adc.txt | 4+++-
ADocumentation/devicetree/bindings/iio/adc/ti-ads124s08.txt | 25+++++++++++++++++++++++++
ADocumentation/devicetree/bindings/iio/chemical/bme680.txt | 11+++++++++++
ADocumentation/devicetree/bindings/iio/chemical/plantower,pms7003.txt | 20++++++++++++++++++++
ADocumentation/devicetree/bindings/iio/chemical/sensirion,sgp30.txt | 15+++++++++++++++
ADocumentation/devicetree/bindings/iio/chemical/sensirion,sps30.txt | 12++++++++++++
ADocumentation/devicetree/bindings/iio/dac/ti,dac7612.txt | 28++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/iio/impedance-analyzer/ad5933.txt | 26++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/imu/bmi160.txt | 6++++--
MDocumentation/devicetree/bindings/iio/imu/inv_mpu6050.txt | 1+
ADocumentation/devicetree/bindings/iio/light/max44009.txt | 24++++++++++++++++++++++++
MDocumentation/devicetree/bindings/iio/st-sensors.txt | 1+
MDocumentation/devicetree/bindings/vendor-prefixes.txt | 1+
MMAINTAINERS | 28+++++++++++++++++++++++-----
Mdrivers/iio/accel/adxl345_core.c | 4++--
Mdrivers/iio/accel/mma8452.c | 105++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mdrivers/iio/accel/st_accel_core.c | 171++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdrivers/iio/adc/Kconfig | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mdrivers/iio/adc/Makefile | 7+++++++
Mdrivers/iio/adc/ad7476.c | 20++++++++++++++++++++
Adrivers/iio/adc/ad7606.c | 583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/adc/ad7606.h | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/adc/ad7606_par.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/adc/ad7606_spi.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/adc/ad7768-1.c | 655+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/exynos_adc.c | 19++++++++++++++++++-
Adrivers/iio/adc/ingenic-adc.c | 364+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/lpc32xx_adc.c | 15+--------------
Mdrivers/iio/adc/meson_saradc.c | 33+++++++++++++++++++++++++++++++++
Adrivers/iio/adc/npcm_adc.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/adc/ti-ads124s08.c | 376+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/adc/xilinx-xadc-core.c | 4+++-
Mdrivers/iio/chemical/Kconfig | 21+++++++++++++++++++++
Mdrivers/iio/chemical/Makefile | 3+++
Mdrivers/iio/chemical/bme680_i2c.c | 7+++++++
Mdrivers/iio/chemical/bme680_spi.c | 8++++++++
Adrivers/iio/chemical/pms7003.c | 340+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/chemical/sgp30.c | 591+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/iio/chemical/sps30.c | 548+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/dac/Kconfig | 16+++++++++++++---
Mdrivers/iio/dac/Makefile | 1+
Mdrivers/iio/dac/ad5686-spi.c | 9++++++---
Mdrivers/iio/dac/ad5686.c | 44+++++++++++++++++++++++++++++++++++++++++---
Mdrivers/iio/dac/ad5686.h | 4+++-
Mdrivers/iio/dac/ad5696-i2c.c | 2+-
Mdrivers/iio/dac/ad5758.c | 2+-
Adrivers/iio/dac/ti-dac7612.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/frequency/ad9523.c | 7+++++--
Mdrivers/iio/imu/bmi160/bmi160.h | 11+++++++++++
Mdrivers/iio/imu/bmi160/bmi160_core.c | 317+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdrivers/iio/imu/bmi160/bmi160_i2c.c | 5+----
Mdrivers/iio/imu/bmi160/bmi160_spi.c | 4+---
Mdrivers/iio/imu/inv_mpu6050/Kconfig | 8++++----
Mdrivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 31+++++++++++++++++++++++++++++++
Mdrivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 6++++++
Mdrivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 8++++++++
Mdrivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 12+++++++++---
Mdrivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 2--
Mdrivers/iio/industrialio-core.c | 5+++++
Mdrivers/iio/light/Kconfig | 10++++++++++
Mdrivers/iio/light/Makefile | 1+
Mdrivers/iio/light/isl29018.c | 46+++++++++++++++++++++++++++++++++++++++++++++-
Adrivers/iio/light/max44009.c | 555+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/magnetometer/mag3110.c | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mdrivers/iio/pressure/Kconfig | 2+-
Mdrivers/iio/pressure/st_pressure.h | 2++
Mdrivers/iio/pressure/st_pressure_core.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/iio/pressure/st_pressure_i2c.c | 5+++++
Mdrivers/iio/pressure/st_pressure_spi.c | 5+++++
Mdrivers/staging/Kconfig | 6++++--
Mdrivers/staging/Makefile | 13+++++++------
Mdrivers/staging/android/ashmem.c | 70++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mdrivers/staging/android/ion/Makefile | 2+-
Ddrivers/staging/android/ion/ion-ioctl.c | 98-------------------------------------------------------------------------------
Mdrivers/staging/android/ion/ion.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdrivers/staging/android/ion/ion.h | 42++++++++----------------------------------
Mdrivers/staging/android/ion/ion_carveout_heap.c | 19+++++++------------
Mdrivers/staging/android/ion/ion_chunk_heap.c | 25+++++++++----------------
Mdrivers/staging/android/ion/ion_cma_heap.c | 6+-----
Mdrivers/staging/android/ion/ion_heap.c | 8+++++++-
Mdrivers/staging/android/ion/ion_page_pool.c | 2+-
Mdrivers/staging/android/ion/ion_system_heap.c | 10+++++++---
Mdrivers/staging/android/uapi/ion.h | 2+-
Mdrivers/staging/android/vsoc.c | 1-
Mdrivers/staging/comedi/comedi_fops.c | 3+--
Mdrivers/staging/comedi/drivers/cb_pcimdas.c | 6+++---
Mdrivers/staging/comedi/drivers/ni_660x.c | 1+
Mdrivers/staging/comedi/drivers/ni_pcidio.c | 444++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mdrivers/staging/comedi/drivers/ni_tio.c | 71+++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mdrivers/staging/comedi/drivers/ni_tio.h | 4++--
Mdrivers/staging/comedi/drivers/usbduxfast.c | 2+-
Mdrivers/staging/emxx_udc/emxx_udc.c | 31++++++++++++++++---------------
Mdrivers/staging/emxx_udc/emxx_udc.h | 2++
Adrivers/staging/erofs/Documentation/filesystems/erofs.txt | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/erofs/Makefile | 2+-
Mdrivers/staging/erofs/data.c | 37+++++--------------------------------
Mdrivers/staging/erofs/dir.c | 12+++++-------
Mdrivers/staging/erofs/inode.c | 41+++++++++++++++--------------------------
Mdrivers/staging/erofs/internal.h | 147+++++++++++++++++++++++++++++++------------------------------------------------
Mdrivers/staging/erofs/namei.c | 194+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mdrivers/staging/erofs/super.c | 29++++++++++++++++-------------
Mdrivers/staging/erofs/unzip_vle.c | 165++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdrivers/staging/erofs/unzip_vle.h | 23+++++++++++------------
Mdrivers/staging/erofs/unzip_vle_lz4.c | 21+++++++++------------
Mdrivers/staging/erofs/utils.c | 58+++++++++++++++++++++++++++++++++++++++-------------------
Mdrivers/staging/erofs/xattr.c | 115++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mdrivers/staging/erofs/xattr.h | 10+++++++---
Mdrivers/staging/fbtft/fb_agm1264k-fl.c | 52++++++++++++++++++++++++++--------------------------
Mdrivers/staging/fbtft/fb_bd663474.c | 6+++---
Mdrivers/staging/fbtft/fb_ili9163.c | 6+++---
Mdrivers/staging/fbtft/fb_ili9320.c | 2+-
Mdrivers/staging/fbtft/fb_ili9325.c | 6+++---
Mdrivers/staging/fbtft/fb_ili9340.c | 2+-
Mdrivers/staging/fbtft/fb_pcd8544.c | 4++--
Mdrivers/staging/fbtft/fb_ra8875.c | 4++--
Mdrivers/staging/fbtft/fb_s6d1121.c | 6+++---
Mdrivers/staging/fbtft/fb_sh1106.c | 2+-
Mdrivers/staging/fbtft/fb_ssd1289.c | 6+++---
Mdrivers/staging/fbtft/fb_ssd1305.c | 4++--
Mdrivers/staging/fbtft/fb_ssd1306.c | 4++--
Mdrivers/staging/fbtft/fb_ssd1325.c | 6+++---
Mdrivers/staging/fbtft/fb_ssd1331.c | 10+++++-----
Mdrivers/staging/fbtft/fb_ssd1351.c | 4++--
Mdrivers/staging/fbtft/fb_tinylcd.c | 2+-
Mdrivers/staging/fbtft/fb_tls8204.c | 6+++---
Mdrivers/staging/fbtft/fb_uc1611.c | 4++--
Mdrivers/staging/fbtft/fb_uc1701.c | 6+++---
Mdrivers/staging/fbtft/fb_upd161704.c | 6+++---
Mdrivers/staging/fbtft/fb_watterott.c | 4++--
Mdrivers/staging/fbtft/fbtft-bus.c | 6+++---
Mdrivers/staging/fbtft/fbtft-core.c | 178++++++++++++++-----------------------------------------------------------------
Mdrivers/staging/fbtft/fbtft-io.c | 26+++++++++++++-------------
Mdrivers/staging/fbtft/fbtft.h | 21++++++++++-----------
Mdrivers/staging/fbtft/fbtft_device.c | 344++-----------------------------------------------------------------------------
Mdrivers/staging/fbtft/flexfb.c | 12++++++------
Mdrivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h | 2+-
Mdrivers/staging/fsl-dpaa2/ethsw/dpsw.h | 2+-
Mdrivers/staging/fsl-dpaa2/ethsw/ethsw.h | 2+-
Mdrivers/staging/fwserial/fwserial.c | 1+
Mdrivers/staging/gasket/gasket_interrupt.c | 1-
Mdrivers/staging/goldfish/goldfish_audio.c | 10----------
Mdrivers/staging/greybus/Kconfig | 1+
Mdrivers/staging/greybus/TODO | 2--
Mdrivers/staging/greybus/arche-apb-ctrl.c | 152++++++++++++++++++++++++++++++++-----------------------------------------------
Mdrivers/staging/greybus/arche-platform.c | 124++++++++++++++++++++++++++-----------------------------------------------------
Mdrivers/staging/greybus/audio_topology.c | 1-
Mdrivers/staging/greybus/bundle.c | 2+-
Mdrivers/staging/greybus/connection.h | 2+-
Mdrivers/staging/greybus/control.c | 1-
Mdrivers/staging/greybus/core.c | 2+-
Mdrivers/staging/greybus/gpio.c | 156++++++--------------------------------------------------------------------------
Mdrivers/staging/gs_fpgaboot/gs_fpgaboot.c | 12+-----------
Mdrivers/staging/gs_fpgaboot/gs_fpgaboot.h | 12+-----------
Mdrivers/staging/gs_fpgaboot/io.c | 16++--------------
Mdrivers/staging/gs_fpgaboot/io.h | 12+-----------
Mdrivers/staging/iio/adc/Kconfig | 34----------------------------------
Mdrivers/staging/iio/adc/Makefile | 4----
Mdrivers/staging/iio/adc/ad7280a.c | 243+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Ddrivers/staging/iio/adc/ad7606.c | 563-------------------------------------------------------------------------------
Ddrivers/staging/iio/adc/ad7606.h | 106-------------------------------------------------------------------------------
Ddrivers/staging/iio/adc/ad7606_par.c | 113-------------------------------------------------------------------------------
Ddrivers/staging/iio/adc/ad7606_spi.c | 79-------------------------------------------------------------------------------
Mdrivers/staging/iio/adc/ad7816.c | 7+++----
Mdrivers/staging/iio/addac/adt7316-i2c.c | 6+++---
Mdrivers/staging/iio/addac/adt7316-spi.c | 4++--
Mdrivers/staging/iio/addac/adt7316.c | 143+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mdrivers/staging/iio/cdc/Kconfig | 10----------
Mdrivers/staging/iio/cdc/Makefile | 1-
Ddrivers/staging/iio/cdc/ad7152.c | 552-------------------------------------------------------------------------------
Mdrivers/staging/iio/frequency/ad9834.c | 54++++++++++++++++++++++++++++++------------------------
Mdrivers/staging/iio/frequency/ad9834.h | 28----------------------------
Mdrivers/staging/iio/impedance-analyzer/ad5933.c | 57++++++++++++++++++++++++++++-----------------------------
Mdrivers/staging/ks7010/Makefile | 2+-
Mdrivers/staging/ks7010/TODO | 2--
Mdrivers/staging/ks7010/ks_hostif.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mdrivers/staging/ks7010/ks_wlan_net.c | 2+-
Ddrivers/staging/ks7010/michael_mic.c | 127-------------------------------------------------------------------------------
Ddrivers/staging/ks7010/michael_mic.h | 21---------------------
Mdrivers/staging/media/davinci_vpfe/Makefile | 2+-
Mdrivers/staging/most/Makefile | 2+-
Mdrivers/staging/most/cdev/Makefile | 2+-
Mdrivers/staging/most/cdev/cdev.c | 5++++-
Mdrivers/staging/most/dim2/Makefile | 2+-
Mdrivers/staging/most/i2c/Makefile | 2+-
Mdrivers/staging/most/net/Makefile | 2+-
Mdrivers/staging/most/sound/Makefile | 2+-
Mdrivers/staging/most/usb/Makefile | 2+-
Mdrivers/staging/most/video/Makefile | 2+-
Mdrivers/staging/mt7621-dma/Kconfig | 6------
Mdrivers/staging/mt7621-dma/Makefile | 1-
Mdrivers/staging/mt7621-dma/mtk-hsdma.c | 11+++--------
Ddrivers/staging/mt7621-dma/ralink-gdma.c | 932-------------------------------------------------------------------------------
Mdrivers/staging/mt7621-dts/gbpc1.dts | 4++--
Mdrivers/staging/mt7621-dts/mt7621.dtsi | 59+++++++++++++++++++++++++++++++++++++++++++++--------------
Mdrivers/staging/mt7621-eth/ethtool.c | 1+
Mdrivers/staging/mt7621-eth/ethtool.h | 11++---------
Mdrivers/staging/mt7621-mmc/Kconfig | 2+-
Mdrivers/staging/mt7621-mmc/dbg.c | 1-
Mdrivers/staging/mt7621-mmc/mt6575_sd.h | 2+-
Adrivers/staging/mt7621-pci-phy/Kconfig | 7+++++++
Adrivers/staging/mt7621-pci-phy/Makefile | 1+
Adrivers/staging/mt7621-pci-phy/TODO | 4++++
Adrivers/staging/mt7621-pci-phy/mediatek,mt7621-pci-phy.txt | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 387+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/mt7621-pci/Makefile | 2+-
Mdrivers/staging/mt7621-pci/TODO | 8--------
Mdrivers/staging/mt7621-pci/pci-mt7621.c | 300+++++++++++++++----------------------------------------------------------------
Mdrivers/staging/mt7621-pinctrl/Kconfig | 1+
Mdrivers/staging/mt7621-pinctrl/pinctrl-rt2880.c | 49+++++++------------------------------------------
Mdrivers/staging/mt7621-spi/spi-mt7621.c | 72+++++++++++++++++++++++++++++++++++++++---------------------------------
Mdrivers/staging/netlogic/Kconfig | 2+-
Mdrivers/staging/netlogic/platform_net.c | 51+++++++++++++--------------------------------------
Mdrivers/staging/netlogic/platform_net.h | 32++------------------------------
Mdrivers/staging/netlogic/xlr_net.c | 31++-----------------------------
Mdrivers/staging/netlogic/xlr_net.h | 33+++------------------------------
Mdrivers/staging/octeon-usb/octeon-hcd.h | 2+-
Adrivers/staging/ralink-gdma/Kconfig | 6++++++
Adrivers/staging/ralink-gdma/Makefile | 3+++
Adrivers/staging/ralink-gdma/ralink-gdma.c | 932+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/rtl8188eu/core/rtw_ap.c | 8++++----
Mdrivers/staging/rtl8188eu/core/rtw_cmd.c | 12++++++------
Mdrivers/staging/rtl8188eu/core/rtw_efuse.c | 4+++-
Mdrivers/staging/rtl8188eu/core/rtw_ieee80211.c | 10+++++-----
Mdrivers/staging/rtl8188eu/core/rtw_ioctl_set.c | 12++++++------
Mdrivers/staging/rtl8188eu/core/rtw_mlme.c | 44++++++++++++++++++++++----------------------
Mdrivers/staging/rtl8188eu/core/rtw_mlme_ext.c | 38+++++++++++++++++++-------------------
Mdrivers/staging/rtl8188eu/core/rtw_wlan_util.c | 18+++++++++---------
Mdrivers/staging/rtl8188eu/core/rtw_xmit.c | 2+-
Mdrivers/staging/rtl8188eu/hal/odm.c | 11+----------
Mdrivers/staging/rtl8188eu/hal/rtl8188e_cmd.c | 67+++++++++++++++++++++++++++++++++----------------------------------
Mdrivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c | 4++--
Mdrivers/staging/rtl8188eu/include/odm.h | 1-
Mdrivers/staging/rtl8188eu/include/odm_hwconfig.h | 9---------
Mdrivers/staging/rtl8188eu/include/wifi.h | 12------------
Mdrivers/staging/rtl8188eu/include/wlan_bssdef.h | 6+++---
Mdrivers/staging/rtl8188eu/os_dep/ioctl_linux.c | 34+++++++++++++++++-----------------
Mdrivers/staging/rtl8188eu/os_dep/mlme_linux.c | 72++++++++++++++++++++++++++++++++++++++----------------------------------
Mdrivers/staging/rtl8188eu/os_dep/os_intfs.c | 26+++++++++++++-------------
Mdrivers/staging/rtl8188eu/os_dep/rtw_android.c | 2+-
Mdrivers/staging/rtl8188eu/os_dep/usb_intf.c | 6+++---
Mdrivers/staging/rtl8192e/dot11d.c | 120++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdrivers/staging/rtl8192e/dot11d.h | 77+++++++++++++++++++++++++++++++++++------------------------------------------
Mdrivers/staging/rtl8192e/rtl8192e/r8192E_dev.c | 8++++----
Mdrivers/staging/rtl8192e/rtl8192e/rtl_core.c | 23+++++++----------------
Mdrivers/staging/rtl8192e/rtl8192e/rtl_dm.c | 2+-
Mdrivers/staging/rtl8192e/rtl8192e/rtl_wx.c | 8++++----
Mdrivers/staging/rtl8192e/rtl819x_BAProc.c | 6+++---
Mdrivers/staging/rtl8192e/rtllib.h | 6+++---
Mdrivers/staging/rtl8192e/rtllib_crypt_tkip.c | 2+-
Mdrivers/staging/rtl8192e/rtllib_rx.c | 4++--
Mdrivers/staging/rtl8192e/rtllib_softmac.c | 22+++++++++++-----------
Mdrivers/staging/rtl8192e/rtllib_wx.c | 4++--
Mdrivers/staging/rtl8192u/Makefile | 2+-
Mdrivers/staging/rtl8192u/r8192U_core.c | 24+++++++++++-------------
Mdrivers/staging/rtl8712/ieee80211.c | 2+-
Mdrivers/staging/rtl8712/rtl8712_efuse.c | 2+-
Mdrivers/staging/rtl8712/rtl8712_led.c | 6+++---
Mdrivers/staging/rtl8712/rtl871x_mlme.c | 2+-
Mdrivers/staging/rtl8712/rtl871x_recv.c | 6+++---
Mdrivers/staging/rtl8712/rtl871x_sta_mgt.c | 2+-
Mdrivers/staging/rtl8712/rtl871x_xmit.c | 8++++----
Mdrivers/staging/rtl8712/usb_intf.c | 16----------------
Mdrivers/staging/rtl8723bs/core/rtw_cmd.c | 8--------
Mdrivers/staging/rtl8723bs/core/rtw_xmit.c | 2+-
Mdrivers/staging/rtl8723bs/include/drv_types.h | 1-
Mdrivers/staging/rtlwifi/Kconfig | 2+-
Mdrivers/staging/rtlwifi/efuse.c | 6++----
Mdrivers/staging/rtlwifi/halmac/halmac_88xx/halmac_func_88xx.c | 5++++-
Mdrivers/staging/rtlwifi/pci.h | 3++-
Mdrivers/staging/rtlwifi/phydm/phydm_rainfo.c | 2+-
Mdrivers/staging/rtlwifi/rtl8822be/fw.c | 2++
Mdrivers/staging/rts5208/ms.c | 5++++-
Mdrivers/staging/rts5208/sd.c | 7++++++-
Mdrivers/staging/sm750fb/ddk750_display.c | 6+++---
Mdrivers/staging/speakup/Kconfig | 32++++++++++++++++----------------
Mdrivers/staging/speakup/kobjects.c | 2++
Mdrivers/staging/speakup/main.c | 14+++++++++-----
Mdrivers/staging/speakup/speakup_decext.c | 3++-
Mdrivers/staging/speakup/speakup_dectlk.c | 3++-
Mdrivers/staging/speakup/speakup_soft.c | 4+++-
Mdrivers/staging/speakup/spk_priv_keyinfo.h | 9+++++++--
Mdrivers/staging/speakup/spk_ttyio.c | 20++++++++++++--------
Mdrivers/staging/speakup/varhandlers.c | 4+++-
Mdrivers/staging/unisys/visorhba/Makefile | 3+--
Mdrivers/staging/unisys/visornic/Makefile | 3+--
Mdrivers/staging/unisys/visornic/visornic_main.c | 4+---
Mdrivers/staging/vc04_services/bcm2835-audio/Makefile | 3+--
Mdrivers/staging/vc04_services/bcm2835-camera/Makefile | 2+-
Mdrivers/staging/vt6655/baseband.c | 10+++-------
Mdrivers/staging/vt6655/baseband.h | 2+-
Mdrivers/staging/vt6655/card.c | 16++++------------
Mdrivers/staging/vt6655/card.h | 2+-
Mdrivers/staging/vt6655/device_main.c | 4++--
Mdrivers/staging/vt6656/key.c | 4+++-
Mdrivers/staging/vt6656/mac.h | 2+-
Mdrivers/staging/wilc1000/Makefile | 2+-
Mdrivers/staging/wilc1000/host_interface.c | 1164++++++++++++++++++++++++-------------------------------------------------------
Mdrivers/staging/wilc1000/host_interface.h | 165+++++++++++++++++++++++++------------------------------------------------------
Ddrivers/staging/wilc1000/linux_mon.c | 273-------------------------------------------------------------------------------
Ddrivers/staging/wilc1000/linux_wlan.c | 1143-------------------------------------------------------------------------------
Adrivers/staging/wilc1000/wilc_mon.c | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/staging/wilc1000/wilc_netdev.c | 1079+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/staging/wilc1000/wilc_sdio.c | 187++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mdrivers/staging/wilc1000/wilc_spi.c | 4++--
Mdrivers/staging/wilc1000/wilc_wfi_cfgoperations.c | 644+++++++++++++++++++++++--------------------------------------------------------
Mdrivers/staging/wilc1000/wilc_wfi_cfgoperations.h | 7++++---
Mdrivers/staging/wilc1000/wilc_wfi_netdevice.h | 14++++++++------
Mdrivers/staging/wilc1000/wilc_wlan.c | 52++++++++++++++++++++++++----------------------------
Mdrivers/staging/wilc1000/wilc_wlan.h | 38+++++++++++++++++++++++++++-----------
Mdrivers/staging/wilc1000/wilc_wlan_cfg.c | 39+++++++++++++++++++--------------------
Mdrivers/staging/wilc1000/wilc_wlan_if.h | 40----------------------------------------
Mdrivers/staging/wlan-ng/Kconfig | 2+-
Mdrivers/staging/wlan-ng/cfg80211.c | 3++-
Mdrivers/staging/wlan-ng/prism2fw.c | 5++---
Ddrivers/staging/xgifb/Kconfig | 11-----------
Ddrivers/staging/xgifb/Makefile | 4----
Ddrivers/staging/xgifb/TODO | 13-------------
Ddrivers/staging/xgifb/XGI_main.h | 365-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/XGI_main_26.c | 2084-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/XGIfb.h | 109-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/vb_def.h | 257-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/vb_init.c | 1367-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/vb_init.h | 6------
Ddrivers/staging/xgifb/vb_setmode.c | 5528-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/vb_setmode.h | 24------------------------
Ddrivers/staging/xgifb/vb_struct.h | 165-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/vb_table.h | 2513-------------------------------------------------------------------------------
Ddrivers/staging/xgifb/vb_util.h | 46----------------------------------------------
Ddrivers/staging/xgifb/vgatypes.h | 51---------------------------------------------------
Ainclude/dt-bindings/iio/adc/ingenic,adc.h | 10++++++++++
Minclude/linux/iio/common/st_sensors.h | 1+
Minclude/uapi/linux/iio/types.h | 7+++++++
Mtools/iio/iio_event_monitor.c | 14++++++++++++++
342 files changed, 12336 insertions(+), 21357 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio @@ -1554,6 +1554,10 @@ What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_ethanol_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_ethanol_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_h2_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_h2_raw What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw KernelVersion: 4.3 @@ -1684,4 +1688,19 @@ KernelVersion: 4.18 Contact: linux-iio@vger.kernel.org Description: Raw (unscaled) phase difference reading from channel Y - that can be processed to radians.- \ No newline at end of file + that can be processed to radians. + +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm2p5_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm2p5_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm4_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm4_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm10_input +What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm10_input +KernelVersion: 4.22 +Contact: linux-iio@vger.kernel.org +Description: + Mass concentration reading of particulate matter in ug / m3. + pmX consists of particles with aerodynamic diameter less or + equal to X micrometers. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-sps30 b/Documentation/ABI/testing/sysfs-bus-iio-sps30 @@ -0,0 +1,28 @@ +What: /sys/bus/iio/devices/iio:deviceX/start_cleaning +Date: December 2018 +KernelVersion: 4.22 +Contact: linux-iio@vger.kernel.org +Description: + Writing 1 starts sensor self cleaning. Internal fan accelerates + to its maximum speed and keeps spinning for about 10 seconds in + order to blow out accumulated dust. + +What: /sys/bus/iio/devices/iio:deviceX/cleaning_period +Date: January 2019 +KernelVersion: 5.1 +Contact: linux-iio@vger.kernel.org +Description: + Sensor is capable of triggering self cleaning periodically. + Period can be changed by writing a new value here. Upon reading + the current one is returned. Units are seconds. + + Writing 0 disables periodical self cleaning entirely. + +What: /sys/bus/iio/devices/iio:deviceX/cleaning_period_available +Date: January 2019 +KernelVersion: 5.1 +Contact: linux-iio@vger.kernel.org +Description: + The range of available values in seconds represented as the + minimum value, the step and the maximum value, all enclosed in + square brackets. diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt @@ -20,6 +20,10 @@ Optional properties: - interrupt-names: should contain "INT1" and/or "INT2", the accelerometer's interrupt line in use. + - vdd-supply: phandle to the regulator that provides vdd power to the accelerometer. + + - vddio-supply: phandle to the regulator that provides vddio power to the accelerometer. + Example: mma8453fc@1d { diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt @@ -0,0 +1,65 @@ +Analog Devices AD7606 Simultaneous Sampling ADC + +Required properties for the AD7606: + +- compatible: Must be one of + * "adi,ad7605-4" + * "adi,ad7606-8" + * "adi,ad7606-6" + * "adi,ad7606-4" +- reg: SPI chip select number for the device +- spi-max-frequency: Max SPI frequency to use + see: Documentation/devicetree/bindings/spi/spi-bus.txt +- spi-cpha: See Documentation/devicetree/bindings/spi/spi-bus.txt +- avcc-supply: phandle to the Avcc power supply +- interrupts: IRQ line for the ADC + see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt +- adi,conversion-start-gpios: must be the device tree identifier of the CONVST pin. + This logic input is used to initiate conversions on the analog + input channels. As the line is active high, it should be marked + GPIO_ACTIVE_HIGH. + +Optional properties: + +- reset-gpios: must be the device tree identifier of the RESET pin. If specified, + it will be asserted during driver probe. As the line is active high, + it should be marked GPIO_ACTIVE_HIGH. +- standby-gpios: must be the device tree identifier of the STBY pin. This pin is used + to place the AD7606 into one of two power-down modes, Standby mode or + Shutdown mode. As the line is active low, it should be marked + GPIO_ACTIVE_LOW. +- adi,first-data-gpios: must be the device tree identifier of the FRSTDATA pin. + The FRSTDATA output indicates when the first channel, V1, is + being read back on either the parallel, byte or serial interface. + As the line is active high, it should be marked GPIO_ACTIVE_HIGH. +- adi,range-gpios: must be the device tree identifier of the RANGE pin. The polarity on + this pin determines the input range of the analog input channels. If + this pin is tied to a logic high, the analog input range is ±10V for + all channels. If this pin is tied to a logic low, the analog input range + is ±5V for all channels. As the line is active high, it should be marked + GPIO_ACTIVE_HIGH. +- adi,oversampling-ratio-gpios: must be the device tree identifier of the over-sampling + mode pins. As the line is active high, it should be marked + GPIO_ACTIVE_HIGH. + +Example: + + adc@0 { + compatible = "adi,ad7606-8"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpol; + + avcc-supply = <&adc_vref>; + + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + + adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>; + adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; + adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH + &gpio 23 GPIO_ACTIVE_HIGH + &gpio 26 GPIO_ACTIVE_HIGH>; + standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt @@ -0,0 +1,41 @@ +Analog Devices AD7768-1 ADC device driver + +Required properties for the AD7768-1: + +- compatible: Must be "adi,ad7768-1" +- reg: SPI chip select number for the device +- spi-max-frequency: Max SPI frequency to use + see: Documentation/devicetree/bindings/spi/spi-bus.txt +- clocks: phandle to the master clock (mclk) + see: Documentation/devicetree/bindings/clock/clock-bindings.txt +- clock-names: Must be "mclk". +- interrupts: IRQ line for the ADC + see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt +- vref-supply: vref supply can be used as reference for conversion +- adi,sync-in-gpios: must be the device tree identifier of the SYNC-IN pin. Enables + synchronization of multiple devices that require simultaneous sampling. + A pulse is always required if the configuration is changed in any way, for example + if the filter decimation rate changes. As the line is active low, it should + be marked GPIO_ACTIVE_LOW. + +Optional properties: + + - reset-gpios : GPIO spec for the RESET pin. If specified, it will be asserted during + driver probe. As the line is active low, it should be marked GPIO_ACTIVE_LOW. + +Example: + + adc@0 { + compatible = "adi,ad7768-1"; + reg = <0>; + spi-max-frequency = <2000000>; + spi-cpol; + spi-cpha; + vref-supply = <&adc_vref>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&gpio>; + adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>; + clocks = <&ad7768_mclk>; + clock-names = "mclk"; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt @@ -23,6 +23,10 @@ Required properties: - #io-channel-cells: must be 1, see ../iio-bindings.txt Optional properties: +- amlogic,hhi-sysctrl: phandle to the syscon which contains the 5th bit + of the TSC (temperature sensor coefficient) on + Meson8b and Meson8m2 (which used to calibrate the + temperature sensor) - nvmem-cells: phandle to the temperature_calib eFuse cells - nvmem-cell-names: if present (to enable the temperature sensor calibration) this must contain "temperature_calib" diff --git a/Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt b/Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt @@ -0,0 +1,48 @@ +* Ingenic JZ47xx ADC controller IIO bindings + +Required properties: + +- compatible: Should be one of: + * ingenic,jz4725b-adc + * ingenic,jz4740-adc +- reg: ADC controller registers location and length. +- clocks: phandle to the SoC's ADC clock. +- clock-names: Must be set to "adc". +- #io-channel-cells: Must be set to <1> to indicate channels are selected + by index. + +ADC clients must use the format described in iio-bindings.txt, giving +a phandle and IIO specifier pair ("io-channels") to the ADC controller. + +Example: + +#include <dt-bindings/iio/adc/ingenic,adc.h> + +adc: adc@10070000 { + compatible = "ingenic,jz4740-adc"; + #io-channel-cells = <1>; + + reg = <0x10070000 0x30>; + + clocks = <&cgu JZ4740_CLK_ADC>; + clock-names = "adc"; + + interrupt-parent = <&intc>; + interrupts = <18>; +}; + +adc-keys { + ... + compatible = "adc-keys"; + io-channels = <&adc INGENIC_ADC_AUX>; + io-channel-names = "buttons"; + ... +}; + +battery { + ... + compatible = "ingenic,jz4740-battery"; + io-channels = <&adc INGENIC_ADC_BATTERY>; + io-channel-names = "battery"; + ... +}; diff --git a/Documentation/devicetree/bindings/staging/iio/adc/lpc32xx-adc.txt b/Documentation/devicetree/bindings/iio/adc/lpc32xx-adc.txt diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt @@ -0,0 +1,24 @@ +Nuvoton NPCM Analog to Digital Converter (ADC) + +The NPCM ADC is a 10-bit converter for eight channel inputs. + +Required properties: +- compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC. +- reg: specifies physical base address and size of the registers. +- interrupts: Contain the ADC interrupt with flags for falling edge. + +Optional properties: +- clocks: phandle of ADC reference clock, in case the clock is not + added the ADC will use the default ADC sample rate. +- vref-supply: The regulator supply ADC reference voltage, in case the + vref-supply is not added the ADC will use internal voltage + reference. + +Example: + +adc: adc@f000c000 { + compatible = "nuvoton,npcm750-adc"; + reg = <0xf000c000 0x8>; + interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk NPCM7XX_CLK_ADC>; +}; diff --git a/Documentation/devicetree/bindings/iio/adc/samsung,exynos-adc.txt b/Documentation/devicetree/bindings/iio/adc/samsung,exynos-adc.txt @@ -11,11 +11,13 @@ New driver handles the following Required properties: - compatible: Must be "samsung,exynos-adc-v1" - for exynos4412/5250 controllers. + for Exynos5250 controllers. Must be "samsung,exynos-adc-v2" for future controllers. Must be "samsung,exynos3250-adc" for controllers compatible with ADC of Exynos3250. + Must be "samsung,exynos4212-adc" for + controllers compatible with ADC of Exynos4212 and Exynos4412. Must be "samsung,exynos7-adc" for the ADC in Exynos7 and compatibles Must be "samsung,s3c2410-adc" for diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt @@ -0,0 +1,25 @@ +* Texas Instruments' ads124s08 and ads124s06 ADC chip + +Required properties: + - compatible : + "ti,ads124s08" + "ti,ads124s06" + - reg : spi chip select number for the device + +Recommended properties: + - spi-max-frequency : Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + - spi-cpha : Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional properties: + - reset-gpios : GPIO pin used to reset the device. + +Example: +adc@0 { + compatible = "ti,ads124s08"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpha; + reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/bme680.txt b/Documentation/devicetree/bindings/iio/chemical/bme680.txt @@ -0,0 +1,11 @@ +Bosch Sensortec BME680 pressure/temperature/humidity/voc sensors + +Required properties: +- compatible: must be "bosch,bme680" + +Example: + +bme680@76 { + compatible = "bosch,bme680"; + reg = <0x76>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/plantower,pms7003.txt b/Documentation/devicetree/bindings/iio/chemical/plantower,pms7003.txt @@ -0,0 +1,20 @@ +* Plantower PMS7003 particulate matter sensor + +Required properties: +- compatible: must be "plantower,pms7003" +- vcc-supply: phandle to the regulator that provides power to the sensor + +Optional properties: +- plantower,set-gpios: phandle to the GPIO connected to the SET line +- reset-gpios: phandle to the GPIO connected to the RESET line + +Refer to serial/slave-device.txt for generic serial attached device bindings. + +Example: + +&uart0 { + air-pollution-sensor { + compatible = "plantower,pms7003"; + vcc-supply = <&reg_vcc5v0>; + }; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp30.txt b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp30.txt @@ -0,0 +1,15 @@ +* Sensirion SGP30/SGPC3 multi-pixel Gas Sensor + +Required properties: + + - compatible: must be one of + "sensirion,sgp30" + "sensirion,sgpc3" + - reg: the I2C address of the sensor + +Example: + +gas@58 { + compatible = "sensirion,sgp30"; + reg = <0x58>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.txt b/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.txt @@ -0,0 +1,12 @@ +* Sensirion SPS30 particulate matter sensor + +Required properties: +- compatible: must be "sensirion,sps30" +- reg: the I2C address of the sensor + +Example: + +sps30@69 { + compatible = "sensirion,sps30"; + reg = <0x69>; +}; diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt @@ -0,0 +1,28 @@ +* Texas Instruments Dual, 12-Bit Serial Input Digital-to-Analog Converter + +The DAC7612 is a dual, 12-bit digital-to-analog converter (DAC) with guaranteed +12-bit monotonicity performance over the industrial temperature range. +Is is programmable through an SPI interface. + +The internal DACs are loaded when the LOADDACS pin is pulled down. + +http://www.ti.com/lit/ds/sbas106/sbas106.pdf + +Required Properties: +- compatible: Should be one of: + "ti,dac7612" + "ti,dac7612u" + "ti,dac7612ub" +- reg: Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional Properties: +- ti,loaddacs-gpios: GPIO descriptor for the LOADDACS pin. +- spi-*: Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + + dac@1 { + compatible = "ti,dac7612"; + reg = <0x1>; + ti,loaddacs-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>; + }; diff --git a/Documentation/devicetree/bindings/iio/impedance-analyzer/ad5933.txt b/Documentation/devicetree/bindings/iio/impedance-analyzer/ad5933.txt @@ -0,0 +1,26 @@ +Analog Devices AD5933/AD5934 Impedance Converter, Network Analyzer + +https://www.analog.com/media/en/technical-documentation/data-sheets/AD5933.pdf +https://www.analog.com/media/en/technical-documentation/data-sheets/AD5934.pdf + +Required properties: + - compatible : should be one of + "adi,ad5933" + "adi,ad5934" + - reg : the I2C address. + - vdd-supply : The regulator supply for DVDD, AVDD1 and AVDD2 when they + are connected together. + +Optional properties: +- clocks : external clock reference. +- clock-names : must be "mclk" if clocks is set. + +Example for a I2C device node: + + impedance-analyzer@0d { + compatible = "adi,adxl345"; + reg = <0x0d>; + vdd-supply = <&vdd_supply>; + clocks = <&ref_clk>; + clock-names = "mclk"; + }; diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.txt b/Documentation/devicetree/bindings/iio/imu/bmi160.txt @@ -9,9 +9,11 @@ Required properties: - spi-max-frequency : set maximum clock frequency (only for SPI) Optional properties: - - interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW + - interrupts : interrupt mapping for IRQ - interrupt-names : set to "INT1" if INT1 pin should be used as interrupt input, set to "INT2" if INT2 pin should be used instead + - drive-open-drain : set if the specified interrupt pin should be configured as + open drain. If not set, defaults to push-pull. Examples: @@ -20,7 +22,7 @@ bmi160@68 { reg = <0x68>; interrupt-parent = <&gpio4>; - interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + interrupts = <12 IRQ_TYPE_EDGE_RISING>; interrupt-names = "INT1"; }; diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -11,6 +11,7 @@ Required properties: "invensense,mpu9250" "invensense,mpu9255" "invensense,icm20608" + "invensense,icm20602" - reg : the I2C address of the sensor - interrupts: interrupt mapping for IRQ. It should be configured with flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or diff --git a/Documentation/devicetree/bindings/iio/light/max44009.txt b/Documentation/devicetree/bindings/iio/light/max44009.txt @@ -0,0 +1,24 @@ +* MAX44009 Ambient Light Sensor + +Required properties: + +- compatible: should be "maxim,max44009" +- reg: the I2C address of the device (default is <0x4a>) + +Optional properties: + +- interrupts: interrupt mapping for GPIO IRQ. Should be configured with + IRQ_TYPE_EDGE_FALLING. + +Refer to interrupt-controller/interrupts.txt for generic interrupt client +node bindings. + +Example: + +light-sensor@4a { + compatible = "maxim,max44009"; + reg = <0x4a>; + + interrupt-parent = <&gpio1>; + interrupts = <17 IRQ_TYPE_EDGE_FALLING>; +}; diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -77,3 +77,4 @@ Pressure sensors: - st,lps22hb-press - st,lps33hw - st,lps35hw +- st,lps22hh diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -310,6 +310,7 @@ phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd pine64 Pine64 pixcir PIXCIR MICROELECTRONICS Co., Ltd +plantower Plantower Co., Ltd plathome Plat'Home Co., Ltd. plda PLDA plx Broadcom Corporation (formerly PLX Technology) diff --git a/MAINTAINERS b/MAINTAINERS @@ -854,6 +854,22 @@ S: Supported F: drivers/iio/adc/ad7124.c F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt +ANALOG DEVICES INC AD7606 DRIVER +M: Stefan Popa <stefan.popa@analog.com> +L: linux-iio@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/adc/ad7606.c +F: Documentation/devicetree/bindings/iio/adc/ad7606.txt + +ANALOG DEVICES INC AD7768-1 DRIVER +M: Stefan Popa <stefan.popa@analog.com> +L: linux-iio@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/adc/ad7768-1.c +F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt + ANALOG DEVICES INC AD9389B DRIVER M: Hans Verkuil <hans.verkuil@cisco.com> L: linux-media@vger.kernel.org @@ -14619,11 +14635,6 @@ L: linux-wireless@vger.kernel.org S: Supported F: drivers/staging/wilc1000/ -STAGING - XGI Z7,Z9,Z11 PCI DISPLAY DRIVER -M: Arnaud Patard <arnaud.patard@rtp-net.org> -S: Odd Fixes -F: drivers/staging/xgifb/ - STAGING SUBSYSTEM M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git @@ -15218,6 +15229,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/soc/ti/ +Texas Instruments' DAC7612 DAC Driver +M: Ricardo Ribalda <ricardo@ribalda.com> +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/dac/ti-dac7612.c +F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt + THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER M: Hans Verkuil <hverkuil@xs4all.nl> L: linux-media@vger.kernel.org diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c @@ -150,8 +150,8 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, } static int adxl345_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct adxl345_data *data = iio_priv(indio_dev); s64 n; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c @@ -31,6 +31,7 @@ #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #define MMA8452_STATUS 0x00 #define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) @@ -107,6 +108,8 @@ struct mma8452_data { u8 data_cfg; const struct mma_chip_info *chip_info; int sleep_val; + struct regulator *vdd_reg; + struct regulator *vddio_reg; }; /** @@ -1534,9 +1537,39 @@ static int mma8452_probe(struct i2c_client *client, mutex_init(&data->lock); data->chip_info = match->data; + data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_reg)) { + if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(&client->dev, "failed to get VDD regulator!\n"); + return PTR_ERR(data->vdd_reg); + } + + data->vddio_reg = devm_regulator_get(&client->dev, "vddio"); + if (IS_ERR(data->vddio_reg)) { + if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(&client->dev, "failed to get VDDIO regulator!\n"); + return PTR_ERR(data->vddio_reg); + } + + ret = regulator_enable(data->vdd_reg); + if (ret) { + dev_err(&client->dev, "failed to enable VDD regulator!\n"); + return ret; + } + + ret = regulator_enable(data->vddio_reg); + if (ret) { + dev_err(&client->dev, "failed to enable VDDIO regulator!\n"); + goto disable_regulator_vdd; + } + ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); if (ret < 0) - return ret; + goto disable_regulators; switch (ret) { case MMA8451_DEVICE_ID: @@ -1549,7 +1582,8 @@ static int mma8452_probe(struct i2c_client *client, break; /* else: fall through */ default: - return -ENODEV; + ret = -ENODEV; + goto disable_regulators; } dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n", @@ -1566,13 +1600,13 @@ static int mma8452_probe(struct i2c_client *client, ret = mma8452_reset(client); if (ret < 0) - return ret; + goto disable_regulators; data->data_cfg = MMA8452_DATA_CFG_FS_2G; ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG, data->data_cfg); if (ret < 0) - return ret; + goto disable_regulators; /* * By default set transient threshold to max to avoid events if @@ -1581,7 +1615,7 @@ static int mma8452_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS, MMA8452_TRANSIENT_THS_MASK); if (ret < 0) - return ret; + goto disable_regulators; if (client->irq) { int irq2; @@ -1595,7 +1629,7 @@ static int mma8452_probe(struct i2c_client *client, MMA8452_CTRL_REG5, data->chip_info->all_events); if (ret < 0) - return ret; + goto disable_regulators; dev_dbg(&client->dev, "using interrupt line INT1\n"); } @@ -1604,11 +1638,11 @@ static int mma8452_probe(struct i2c_client *client, MMA8452_CTRL_REG4, data->chip_info->enabled_events); if (ret < 0) - return ret; + goto disable_regulators; ret = mma8452_trigger_setup(indio_dev); if (ret < 0) - return ret; + goto disable_regulators; } data->ctrl_reg1 = MMA8452_CTRL_ACTIVE | @@ -1661,12 +1695,19 @@ buffer_cleanup: trigger_cleanup: mma8452_trigger_cleanup(indio_dev); +disable_regulators: + regulator_disable(data->vddio_reg); + +disable_regulator_vdd: + regulator_disable(data->vdd_reg); + return ret; } static int mma8452_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct mma8452_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); @@ -1678,6 +1719,9 @@ static int mma8452_remove(struct i2c_client *client) mma8452_trigger_cleanup(indio_dev); mma8452_standby(iio_priv(indio_dev)); + regulator_disable(data->vddio_reg); + regulator_disable(data->vdd_reg); + return 0; } @@ -1696,6 +1740,18 @@ static int mma8452_runtime_suspend(struct device *dev) return -EAGAIN; } + ret = regulator_disable(data->vddio_reg); + if (ret) { + dev_err(dev, "failed to disable VDDIO regulator\n"); + return ret; + } + + ret = regulator_disable(data->vdd_reg); + if (ret) { + dev_err(dev, "failed to disable VDD regulator\n"); + return ret; + } + return 0; } @@ -1705,9 +1761,22 @@ static int mma8452_runtime_resume(struct device *dev) struct mma8452_data *data = iio_priv(indio_dev); int ret, sleep_val; + ret = regulator_enable(data->vdd_reg); + if (ret) { + dev_err(dev, "failed to enable VDD regulator\n"); + return ret; + } + + ret = regulator_enable(data->vddio_reg); + if (ret) { + dev_err(dev, "failed to enable VDDIO regulator\n"); + regulator_disable(data->vdd_reg); + return ret; + } + ret = mma8452_active(data); if (ret < 0) - return ret; + goto runtime_resume_failed; ret = mma8452_get_odr_index(data); sleep_val = 1000 / mma8452_samp_freq[ret][0]; @@ -1717,25 +1786,17 @@ static int mma8452_runtime_resume(struct device *dev) msleep_interruptible(sleep_val); return 0; -} -#endif -#ifdef CONFIG_PM_SLEEP -static int mma8452_suspend(struct device *dev) -{ - return mma8452_standby(iio_priv(i2c_get_clientdata( - to_i2c_client(dev)))); -} +runtime_resume_failed: + regulator_disable(data->vddio_reg); + regulator_disable(data->vdd_reg); -static int mma8452_resume(struct device *dev) -{ - return mma8452_active(iio_priv(i2c_get_clientdata( - to_i2c_client(dev)))); + return ret; } #endif static const struct dev_pm_ops mma8452_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mma8452_suspend, mma8452_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(mma8452_runtime_suspend, mma8452_runtime_resume, NULL) }; diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/acpi.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/mutex.h> @@ -918,12 +919,167 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { #define ST_ACCEL_TRIGGER_OPS NULL #endif +static const struct iio_mount_matrix * +get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *adata = iio_priv(indio_dev); + + return adata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, get_mount_matrix), + { }, +}; + +/* Read ST-specific _ONT orientation data from ACPI and generate an + * appropriate mount matrix. + */ +static int apply_acpi_orientation(struct iio_dev *indio_dev, + struct iio_chan_spec *channels) +{ +#ifdef CONFIG_ACPI + struct st_sensor_data *adata = iio_priv(indio_dev); + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_device *adev; + union acpi_object *ont; + union acpi_object *elements; + acpi_status status; + int ret = -EINVAL; + unsigned int val; + int i, j; + int final_ont[3][3] = { { 0 }, }; + + /* For some reason, ST's _ONT translation does not apply directly + * to the data read from the sensor. Another translation must be + * performed first, as described by the matrix below. Perhaps + * ST required this specific translation for the first product + * where the device was mounted? + */ + const int default_ont[3][3] = { + { 0, 1, 0 }, + { -1, 0, 0 }, + { 0, 0, -1 }, + }; + + + adev = ACPI_COMPANION(adata->dev); + if (!adev) + return 0; + + /* Read _ONT data, which should be a package of 6 integers. */ + status = acpi_evaluate_object(adev->handle, "_ONT", NULL, &buffer); + if (status == AE_NOT_FOUND) { + return 0; + } else if (ACPI_FAILURE(status)) { + dev_warn(&indio_dev->dev, "failed to execute _ONT: %d\n", + status); + return status; + } + + ont = buffer.pointer; + if (ont->type != ACPI_TYPE_PACKAGE || ont->package.count != 6) + goto out; + + /* The first 3 integers provide axis order information. + * e.g. 0 1 2 would indicate normal X,Y,Z ordering. + * e.g. 1 0 2 indicates that data arrives in order Y,X,Z. + */ + elements = ont->package.elements; + for (i = 0; i < 3; i++) { + if (elements[i].type != ACPI_TYPE_INTEGER) + goto out; + + val = elements[i].integer.value; + if (val < 0 || val > 2) + goto out; + + /* Avoiding full matrix multiplication, we simply reorder the + * columns in the default_ont matrix according to the + * ordering provided by _ONT. + */ + final_ont[0][i] = default_ont[0][val]; + final_ont[1][i] = default_ont[1][val]; + final_ont[2][i] = default_ont[2][val]; + } + + /* The final 3 integers provide sign flip information. + * 0 means no change, 1 means flip. + * e.g. 0 0 1 means that Z data should be sign-flipped. + * This is applied after the axis reordering from above. + */ + elements += 3; + for (i = 0; i < 3; i++) { + if (elements[i].type != ACPI_TYPE_INTEGER) + goto out; + + val = elements[i].integer.value; + if (val != 0 && val != 1) + goto out; + if (!val) + continue; + + /* Flip the values in the indicated column */ + final_ont[0][i] *= -1; + final_ont[1][i] *= -1; + final_ont[2][i] *= -1; + } + + /* Convert our integer matrix to a string-based iio_mount_matrix */ + adata->mount_matrix = devm_kmalloc(&indio_dev->dev, + sizeof(*adata->mount_matrix), + GFP_KERNEL); + if (!adata->mount_matrix) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + int matrix_val = final_ont[i][j]; + char *str_value; + + switch (matrix_val) { + case -1: + str_value = "-1"; + break; + case 0: + str_value = "0"; + break; + case 1: + str_value = "1"; + break; + default: + goto out; + } + adata->mount_matrix->rotation[i * 3 + j] = str_value; + } + } + + /* Expose the mount matrix via ext_info */ + for (i = 0; i < indio_dev->num_channels; i++) + channels[i].ext_info = mount_matrix_ext_info; + + ret = 0; + dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n"); + +out: + kfree(buffer.pointer); + return ret; +#else /* !CONFIG_ACPI */ + return 0; +#endif +} + int st_accel_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); struct st_sensors_platform_data *pdata = (struct st_sensors_platform_data *)adata->dev->platform_data; int irq = adata->get_irq_data_ready(indio_dev); + struct iio_chan_spec *channels; + size_t channels_size; int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -942,9 +1098,22 @@ int st_accel_common_probe(struct iio_dev *indio_dev) adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; adata->multiread_bit = adata->sensor_settings->multi_read_bit; - indio_dev->channels = adata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec); + channels = devm_kmemdup(&indio_dev->dev, + adata->sensor_settings->ch, + channels_size, GFP_KERNEL); + if (!channels) { + err = -ENOMEM; + goto st_accel_power_off; + } + + if (apply_acpi_orientation(indio_dev, channels)) + dev_warn(&indio_dev->dev, + "failed to apply ACPI orientation data: %d\n", err); + + indio_dev->channels = channels; adata->current_fullscale = (struct st_sensor_fullscale_avl *) &adata->sensor_settings->fs.fs_avl[0]; adata->odr = adata->sensor_settings->odr.odr_avl[0].hz; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig @@ -57,18 +57,48 @@ config AD7298 module will be called ad7298. config AD7476 - tristate "Analog Devices AD7476 and similar 1-channel ADCs driver" + tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD an TI" depends on SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for Analog Devices AD7273, AD7274, AD7276, - AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, - AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC). + Say yes here to build support for the following SPI analog to + digital converters (ADCs): + Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475, + AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910, + AD7920. + Texas Instruments: ADS7866, ADS7867, ADS7868. To compile this driver as a module, choose M here: the module will be called ad7476. +config AD7606 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config AD7606_IFACE_PARALLEL + tristate "Analog Devices AD7606 ADC driver with parallel interface support" + depends on HAS_IOMEM + select AD7606 + help + Say yes here to build parallel interface support for Analog Devices: + ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7606_parallel. + +config AD7606_IFACE_SPI + tristate "Analog Devices AD7606 ADC driver with spi interface support" + depends on SPI + select AD7606 + help + Say yes here to build spi interface support for Analog Devices: + ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7606_spi. + config AD7766 tristate "Analog Devices AD7766/AD7767 ADC driver" depends on SPI_MASTER @@ -81,6 +111,19 @@ config AD7766 To compile this driver as a module, choose M here: the module will be called ad7766. +config AD7768_1 + tristate "Analog Devices AD7768-1 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7768-1 SPI + simultaneously sampling sigma-delta analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7768-1. + config AD7791 tristate "Analog Devices AD7791 ADC driver" depends on SPI @@ -367,6 +410,15 @@ config INA2XX_ADC Say yes here to build support for TI INA2xx family of Power Monitors. This driver is mutually exclusive with the HWMON version. +config INGENIC_ADC + tristate "Ingenic JZ47xx SoCs ADC driver" + depends on MIPS || COMPILE_TEST + help + Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit. + + This driver can also be built as a module. If so, the module will be + called ingenic_adc. + config IMX7D_ADC tristate "Freescale IMX7D ADC driver" depends on ARCH_MXC || COMPILE_TEST @@ -576,6 +628,16 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config NPCM_ADC + tristate "Nuvoton NPCM ADC driver" + depends on ARCH_NPCM || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for Nuvoton NPCM ADC. + + This driver can also be built as a module. If so, the module + will be called npcm_adc. + config PALMAS_GPADC tristate "TI Palmas General Purpose ADC" depends on MFD_PALMAS @@ -908,6 +970,16 @@ config TI_ADS8688 This driver can also be built as a module. If so, the module will be called ti-ads8688. +config TI_ADS124S08 + tristate "Texas Instruments ADS124S08" + depends on SPI && OF + help + If you say yes here you get support for Texas Instruments ADS124S08 + and ADS124S06 ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads124s08. + config TI_AM335X_ADC tristate "TI's AM335X ADC driver" depends on MFD_TI_AM335X_TSCADC && HAS_DMA diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile @@ -11,7 +11,11 @@ obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7298) += ad7298.o obj-$(CONFIG_AD7923) += ad7923.o obj-$(CONFIG_AD7476) += ad7476.o +obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o +obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o +obj-$(CONFIG_AD7606) += ad7606.o obj-$(CONFIG_AD7766) += ad7766.o +obj-$(CONFIG_AD7768_1) += ad7768-1.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o @@ -36,6 +40,7 @@ obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o +obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o @@ -55,6 +60,7 @@ 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_NPCM_ADC) += npcm_adc.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 @@ -81,6 +87,7 @@ obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o +obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c @@ -59,6 +59,9 @@ enum ad7476_supported_device_ids { ID_ADC081S, ID_ADC101S, ID_ADC121S, + ID_ADS7866, + ID_ADS7867, + ID_ADS7868, }; static irqreturn_t ad7476_trigger_handler(int irq, void *p) @@ -157,6 +160,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, #define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \ BIT(IIO_CHAN_INFO_RAW)) #define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0) +#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { [ID_AD7091R] = { @@ -209,6 +214,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { .channel[0] = ADC081S_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, + [ID_ADS7866] = { + .channel[0] = ADS786X_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADS7867] = { + .channel[0] = ADS786X_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADS7868] = { + .channel[0] = ADS786X_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, }; static const struct iio_info ad7476_info = { @@ -314,6 +331,9 @@ static const struct spi_device_id ad7476_id[] = { {"adc081s", ID_ADC081S}, {"adc101s", ID_ADC101S}, {"adc121s", ID_ADC121S}, + {"ads7866", ID_ADS7866}, + {"ads7867", ID_ADS7867}, + {"ads7868", ID_ADS7868}, {} }; MODULE_DEVICE_TABLE(spi, ad7476_id); diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c @@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7606 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/util_macros.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#include "ad7606.h" + +/* + * Scales are computed as 5000/32768 and 10000/32768 respectively, + * so that when applied to the raw values they provide mV values + */ +static const unsigned int scale_avail[2] = { + 152588, 305176 +}; + +static const unsigned int ad7606_oversampling_avail[7] = { + 1, 2, 4, 8, 16, 32, 64, +}; + +static int ad7606_reset(struct ad7606_state *st) +{ + if (st->gpio_reset) { + gpiod_set_value(st->gpio_reset, 1); + ndelay(100); /* t_reset >= 100ns */ + gpiod_set_value(st->gpio_reset, 0); + return 0; + } + + return -ENODEV; +} + +static int ad7606_read_samples(struct ad7606_state *st) +{ + unsigned int num = st->chip_info->num_channels; + u16 *data = st->data; + int ret; + + /* + * The frstdata signal is set to high while and after reading the sample + * of the first channel and low for all other channels. This can be used + * to check that the incoming data is correctly aligned. During normal + * operation the data should never become unaligned, but some glitch or + * electrostatic discharge might cause an extra read or clock cycle. + * Monitoring the frstdata signal allows to recover from such failure + * situations. + */ + + if (st->gpio_frstdata) { + ret = st->bops->read_block(st->dev, 1, data); + if (ret) + return ret; + + if (!gpiod_get_value(st->gpio_frstdata)) { + ad7606_reset(st); + return -EIO; + } + + data++; + num--; + } + + return st->bops->read_block(st->dev, num, data); +} + +static irqreturn_t ad7606_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = ad7606_read_samples(st); + if (ret == 0) + iio_push_to_buffers_with_timestamp(indio_dev, st->data, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + /* The rising edge of the CONVST signal starts a new conversion. */ + gpiod_set_value(st->gpio_convst, 1); + + mutex_unlock(&st->lock); + + return IRQ_HANDLED; +} + +static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch) +{ + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + gpiod_set_value(st->gpio_convst, 1); + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) { + ret = -ETIMEDOUT; + goto error_ret; + } + + ret = ad7606_read_samples(st); + if (ret == 0) + ret = st->data[ch]; + +error_ret: + gpiod_set_value(st->gpio_convst, 0); + + return ret; +} + +static int ad7606_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7606_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ad7606_scan_direct(indio_dev, chan->address); + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + *val = (short)ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = scale_avail[st->range]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static ssize_t in_voltage_scale_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(scale_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + scale_avail[i]); + + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); + +static int ad7606_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + DECLARE_BITMAP(values, 3); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + mutex_lock(&st->lock); + i = find_closest(val2, scale_avail, ARRAY_SIZE(scale_avail)); + gpiod_set_value(st->gpio_range, i); + st->range = i; + mutex_unlock(&st->lock); + + return 0; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val2) + return -EINVAL; + i = find_closest(val, ad7606_oversampling_avail, + ARRAY_SIZE(ad7606_oversampling_avail)); + + values[0] = i; + + mutex_lock(&st->lock); + gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, + st->gpio_os->info, values); + st->oversampling = ad7606_oversampling_avail[i]; + mutex_unlock(&st->lock); + + return 0; + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64"); + +static struct attribute *ad7606_attributes_os_and_range[] = { + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + &iio_const_attr_oversampling_ratio_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7606_attribute_group_os_and_range = { + .attrs = ad7606_attributes_os_and_range, +}; + +static struct attribute *ad7606_attributes_os[] = { + &iio_const_attr_oversampling_ratio_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7606_attribute_group_os = { + .attrs = ad7606_attributes_os, +}; + +static struct attribute *ad7606_attributes_range[] = { + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7606_attribute_group_range = { + .attrs = ad7606_attributes_range, +}; + +#define AD760X_CHANNEL(num, mask) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .address = num, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + .info_mask_shared_by_all = mask, \ + .scan_index = num, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define AD7605_CHANNEL(num) \ + AD760X_CHANNEL(num, 0) + +#define AD7606_CHANNEL(num) \ + AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO)) + +static const struct iio_chan_spec ad7605_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(4), + AD7605_CHANNEL(0), + AD7605_CHANNEL(1), + AD7605_CHANNEL(2), + AD7605_CHANNEL(3), +}; + +static const struct iio_chan_spec ad7606_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(8), + AD7606_CHANNEL(0), + AD7606_CHANNEL(1), + AD7606_CHANNEL(2), + AD7606_CHANNEL(3), + AD7606_CHANNEL(4), + AD7606_CHANNEL(5), + AD7606_CHANNEL(6), + AD7606_CHANNEL(7), +}; + +static const struct ad7606_chip_info ad7606_chip_info_tbl[] = { + /* More devices added in future */ + [ID_AD7605_4] = { + .channels = ad7605_channels, + .num_channels = 5, + }, + [ID_AD7606_8] = { + .channels = ad7606_channels, + .num_channels = 9, + .has_oversampling = true, + }, + [ID_AD7606_6] = { + .channels = ad7606_channels, + .num_channels = 7, + .has_oversampling = true, + }, + [ID_AD7606_4] = { + .channels = ad7606_channels, + .num_channels = 5, + .has_oversampling = true, + }, +}; + +static int ad7606_request_gpios(struct ad7606_state *st) +{ + struct device *dev = st->dev; + + st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_convst)) + return PTR_ERR(st->gpio_convst); + + st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); + + st->gpio_range = devm_gpiod_get_optional(dev, "adi,range", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_range)) + return PTR_ERR(st->gpio_range); + + st->gpio_standby = devm_gpiod_get_optional(dev, "standby", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_standby)) + return PTR_ERR(st->gpio_standby); + + st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data", + GPIOD_IN); + if (IS_ERR(st->gpio_frstdata)) + return PTR_ERR(st->gpio_frstdata); + + if (!st->chip_info->has_oversampling) + return 0; + + st->gpio_os = devm_gpiod_get_array_optional(dev, + "adi,oversampling-ratio", + GPIOD_OUT_LOW); + return PTR_ERR_OR_ZERO(st->gpio_os); +} + +/* + * The BUSY signal indicates when conversions are in progress, so when a rising + * edge of CONVST is applied, BUSY goes logic high and transitions low at the + * end of the entire conversion process. The falling edge of the BUSY signal + * triggers this interrupt. + */ +static irqreturn_t ad7606_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad7606_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) { + gpiod_set_value(st->gpio_convst, 0); + iio_trigger_poll_chained(st->trig); + } else { + complete(&st->completion); + } + + return IRQ_HANDLED; +}; + +static int ad7606_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->trig != trig) + return -EINVAL; + + return 0; +} + +static int ad7606_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + iio_triggered_buffer_postenable(indio_dev); + gpiod_set_value(st->gpio_convst, 1); + + return 0; +} + +static int ad7606_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + gpiod_set_value(st->gpio_convst, 0); + + return iio_triggered_buffer_predisable(indio_dev); +} + +static const struct iio_buffer_setup_ops ad7606_buffer_ops = { + .postenable = &ad7606_buffer_postenable, + .predisable = &ad7606_buffer_predisable, +}; + +static const struct iio_info ad7606_info_no_os_or_range = { + .read_raw = &ad7606_read_raw, + .validate_trigger = &ad7606_validate_trigger, +}; + +static const struct iio_info ad7606_info_os_and_range = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .attrs = &ad7606_attribute_group_os_and_range, + .validate_trigger = &ad7606_validate_trigger, +}; + +static const struct iio_info ad7606_info_os = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .attrs = &ad7606_attribute_group_os, + .validate_trigger = &ad7606_validate_trigger, +}; + +static const struct iio_info ad7606_info_range = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .attrs = &ad7606_attribute_group_range, + .validate_trigger = &ad7606_validate_trigger, +}; + +static const struct iio_trigger_ops ad7606_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static void ad7606_regulator_disable(void *data) +{ + struct ad7606_state *st = data; + + regulator_disable(st->reg); +} + +int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, + const char *name, unsigned int id, + const struct ad7606_bus_ops *bops) +{ + struct ad7606_state *st; + int ret; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + st->dev = dev; + mutex_init(&st->lock); + st->bops = bops; + st->base_address = base_address; + /* tied to logic low, analog input range is +/- 5V */ + st->range = 0; + st->oversampling = 1; + + st->reg = devm_regulator_get(dev, "avcc"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(dev, "Failed to enable specified AVcc supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st); + if (ret) + return ret; + + st->chip_info = &ad7606_chip_info_tbl[id]; + + ret = ad7606_request_gpios(st); + if (ret) + return ret; + + indio_dev->dev.parent = dev; + if (st->gpio_os) { + if (st->gpio_range) + indio_dev->info = &ad7606_info_os_and_range; + else + indio_dev->info = &ad7606_info_os; + } else { + if (st->gpio_range) + indio_dev->info = &ad7606_info_range; + else + indio_dev->info = &ad7606_info_no_os_or_range; + } + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = name; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + init_completion(&st->completion); + + ret = ad7606_reset(st); + if (ret) + dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); + + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7606_trigger_ops; + st->trig->dev.parent = dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_request_threaded_irq(dev, irq, + NULL, + &ad7606_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + name, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad7606_trigger_handler, + &ad7606_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(ad7606_probe); + +#ifdef CONFIG_PM_SLEEP + +static int ad7606_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->gpio_standby) { + gpiod_set_value(st->gpio_range, 1); + gpiod_set_value(st->gpio_standby, 0); + } + + return 0; +} + +static int ad7606_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->gpio_standby) { + gpiod_set_value(st->gpio_range, st->range); + gpiod_set_value(st->gpio_standby, 1); + ad7606_reset(st); + } + + return 0; +} + +SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume); +EXPORT_SYMBOL_GPL(ad7606_pm_ops); + +#endif + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AD7606 ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#ifndef IIO_ADC_AD7606_H_ +#define IIO_ADC_AD7606_H_ + +/** + * struct ad7606_chip_info - chip specific information + * @channels: channel specification + * @num_channels: number of channels + * @has_oversampling: whether the device has oversampling support + */ +struct ad7606_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + bool has_oversampling; +}; + +/** + * struct ad7606_state - driver instance specific data + * @dev pointer to kernel device + * @chip_info entry in the table of chips that describes this device + * @reg regulator info for the the power supply of the device + * @bops bus operations (SPI or parallel) + * @range voltage range selection, selects which scale to apply + * @oversampling oversampling selection + * @base_address address from where to read data in parallel operation + * @lock protect sensor state from concurrent accesses to GPIOs + * @gpio_convst GPIO descriptor for conversion start signal (CONVST) + * @gpio_reset GPIO descriptor for device hard-reset + * @gpio_range GPIO descriptor for range selection + * @gpio_standby GPIO descriptor for stand-by signal (STBY), + * controls power-down mode of device + * @gpio_frstdata GPIO descriptor for reading from device when data + * is being read on the first channel + * @gpio_os GPIO descriptors to control oversampling on the device + * @complete completion to indicate end of conversion + * @trig The IIO trigger associated with the device. + * @data buffer for reading data from the device + */ +struct ad7606_state { + struct device *dev; + const struct ad7606_chip_info *chip_info; + struct regulator *reg; + const struct ad7606_bus_ops *bops; + unsigned int range; + unsigned int oversampling; + void __iomem *base_address; + + struct mutex lock; /* protect sensor state */ + struct gpio_desc *gpio_convst; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_range; + struct gpio_desc *gpio_standby; + struct gpio_desc *gpio_frstdata; + struct gpio_descs *gpio_os; + struct iio_trigger *trig; + struct completion completion; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * 8 * 16-bit samples + 64-bit timestamp + */ + unsigned short data[12] ____cacheline_aligned; +}; + +/** + * struct ad7606_bus_ops - driver bus operations + * @read_block function pointer for reading blocks of data + */ +struct ad7606_bus_ops { + /* more methods added in future? */ + int (*read_block)(struct device *dev, int num, void *data); +}; + +int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, + const char *name, unsigned int id, + const struct ad7606_bus_ops *bops); + +enum ad7606_supported_device_ids { + ID_AD7605_4, + ID_AD7606_8, + ID_AD7606_6, + ID_AD7606_4 +}; + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops ad7606_pm_ops; +#define AD7606_PM_OPS (&ad7606_pm_ops) +#else +#define AD7606_PM_OPS NULL +#endif + +#endif /* IIO_ADC_AD7606_H_ */ diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7606 Parallel Interface ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <linux/iio/iio.h> +#include "ad7606.h" + +static int ad7606_par16_read_block(struct device *dev, + int count, void *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + insw((unsigned long)st->base_address, buf, count); + + return 0; +} + +static const struct ad7606_bus_ops ad7606_par16_bops = { + .read_block = ad7606_par16_read_block, +}; + +static int ad7606_par8_read_block(struct device *dev, + int count, void *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + insb((unsigned long)st->base_address, buf, count * 2); + + return 0; +} + +static const struct ad7606_bus_ops ad7606_par8_bops = { + .read_block = ad7606_par8_read_block, +}; + +static int ad7606_par_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct resource *res; + void __iomem *addr; + resource_size_t remap_size; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq: %d\n", irq); + return irq; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) + return PTR_ERR(addr); + + remap_size = resource_size(res); + + return ad7606_probe(&pdev->dev, irq, addr, + id->name, id->driver_data, + remap_size > 1 ? &ad7606_par16_bops : + &ad7606_par8_bops); +} + +static const struct platform_device_id ad7606_driver_ids[] = { + { .name = "ad7605-4", .driver_data = ID_AD7605_4, }, + { .name = "ad7606-4", .driver_data = ID_AD7606_4, }, + { .name = "ad7606-6", .driver_data = ID_AD7606_6, }, + { .name = "ad7606-8", .driver_data = ID_AD7606_8, }, + { } +}; +MODULE_DEVICE_TABLE(platform, ad7606_driver_ids); + +static const struct of_device_id ad7606_of_match[] = { + { .compatible = "adi,ad7605-4" }, + { .compatible = "adi,ad7606-4" }, + { .compatible = "adi,ad7606-6" }, + { .compatible = "adi,ad7606-8" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7606_of_match); + +static struct platform_driver ad7606_driver = { + .probe = ad7606_par_probe, + .id_table = ad7606_driver_ids, + .driver = { + .name = "ad7606", + .pm = AD7606_PM_OPS, + .of_match_table = ad7606_of_match, + }, +}; +module_platform_driver(ad7606_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7606 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> +#include "ad7606.h" + +#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */ + +static int ad7606_spi_read_block(struct device *dev, + int count, void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + int i, ret; + unsigned short *data = buf; + __be16 *bdata = buf; + + ret = spi_read(spi, buf, count * 2); + if (ret < 0) { + dev_err(&spi->dev, "SPI read error\n"); + return ret; + } + + for (i = 0; i < count; i++) + data[i] = be16_to_cpu(bdata[i]); + + return 0; +} + +static const struct ad7606_bus_ops ad7606_spi_bops = { + .read_block = ad7606_spi_read_block, +}; + +static int ad7606_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + return ad7606_probe(&spi->dev, spi->irq, NULL, + id->name, id->driver_data, + &ad7606_spi_bops); +} + +static const struct spi_device_id ad7606_id_table[] = { + { "ad7605-4", ID_AD7605_4 }, + { "ad7606-4", ID_AD7606_4 }, + { "ad7606-6", ID_AD7606_6 }, + { "ad7606-8", ID_AD7606_8 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7606_id_table); + +static const struct of_device_id ad7606_of_match[] = { + { .compatible = "adi,ad7605-4" }, + { .compatible = "adi,ad7606-4" }, + { .compatible = "adi,ad7606-6" }, + { .compatible = "adi,ad7606-8" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7606_of_match); + +static struct spi_driver ad7606_driver = { + .driver = { + .name = "ad7606", + .of_match_table = ad7606_of_match, + .pm = AD7606_PM_OPS, + }, + .probe = ad7606_spi_probe, + .id_table = ad7606_id_table, +}; +module_spi_driver(ad7606_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768-1 SPI ADC driver + * + * Copyright 2017 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +/* AD7768 registers definition */ +#define AD7768_REG_CHIP_TYPE 0x3 +#define AD7768_REG_PROD_ID_L 0x4 +#define AD7768_REG_PROD_ID_H 0x5 +#define AD7768_REG_CHIP_GRADE 0x6 +#define AD7768_REG_SCRATCH_PAD 0x0A +#define AD7768_REG_VENDOR_L 0x0C +#define AD7768_REG_VENDOR_H 0x0D +#define AD7768_REG_INTERFACE_FORMAT 0x14 +#define AD7768_REG_POWER_CLOCK 0x15 +#define AD7768_REG_ANALOG 0x16 +#define AD7768_REG_ANALOG2 0x17 +#define AD7768_REG_CONVERSION 0x18 +#define AD7768_REG_DIGITAL_FILTER 0x19 +#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A +#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B +#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C +#define AD7768_REG_SYNC_RESET 0x1D +#define AD7768_REG_GPIO_CONTROL 0x1E +#define AD7768_REG_GPIO_WRITE 0x1F +#define AD7768_REG_GPIO_READ 0x20 +#define AD7768_REG_OFFSET_HI 0x21 +#define AD7768_REG_OFFSET_MID 0x22 +#define AD7768_REG_OFFSET_LO 0x23 +#define AD7768_REG_GAIN_HI 0x24 +#define AD7768_REG_GAIN_MID 0x25 +#define AD7768_REG_GAIN_LO 0x26 +#define AD7768_REG_SPI_DIAG_ENABLE 0x28 +#define AD7768_REG_ADC_DIAG_ENABLE 0x29 +#define AD7768_REG_DIG_DIAG_ENABLE 0x2A +#define AD7768_REG_ADC_DATA 0x2C +#define AD7768_REG_MASTER_STATUS 0x2D +#define AD7768_REG_SPI_DIAG_STATUS 0x2E +#define AD7768_REG_ADC_DIAG_STATUS 0x2F +#define AD7768_REG_DIG_DIAG_STATUS 0x30 +#define AD7768_REG_MCLK_COUNTER 0x31 + +/* AD7768_REG_POWER_CLOCK */ +#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4) +#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x) +#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0) +#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x) + +/* AD7768_REG_DIGITAL_FILTER */ +#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4) +#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x) +#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0) +#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x) + +/* AD7768_REG_CONVERSION */ +#define AD7768_CONV_MODE_MSK GENMASK(2, 0) +#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) + +#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F)) +#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F) + +enum ad7768_conv_mode { + AD7768_CONTINUOUS, + AD7768_ONE_SHOT, + AD7768_SINGLE, + AD7768_PERIODIC, + AD7768_STANDBY +}; + +enum ad7768_pwrmode { + AD7768_ECO_MODE = 0, + AD7768_MED_MODE = 2, + AD7768_FAST_MODE = 3 +}; + +enum ad7768_mclk_div { + AD7768_MCLK_DIV_16, + AD7768_MCLK_DIV_8, + AD7768_MCLK_DIV_4, + AD7768_MCLK_DIV_2 +}; + +enum ad7768_dec_rate { + AD7768_DEC_RATE_32 = 0, + AD7768_DEC_RATE_64 = 1, + AD7768_DEC_RATE_128 = 2, + AD7768_DEC_RATE_256 = 3, + AD7768_DEC_RATE_512 = 4, + AD7768_DEC_RATE_1024 = 5, + AD7768_DEC_RATE_8 = 9, + AD7768_DEC_RATE_16 = 10 +}; + +struct ad7768_clk_configuration { + enum ad7768_mclk_div mclk_div; + enum ad7768_dec_rate dec_rate; + unsigned int clk_div; + enum ad7768_pwrmode pwrmode; +}; + +static const struct ad7768_clk_configuration ad7768_clk_config[] = { + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE }, +}; + +static const struct iio_chan_spec ad7768_channels[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .indexed = 1, + .channel = 0, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, + }, +}; + +struct ad7768_state { + struct spi_device *spi; + struct regulator *vref; + struct mutex lock; + struct clk *mclk; + unsigned int mclk_freq; + unsigned int samp_freq; + struct completion completion; + struct iio_trigger *trig; + struct gpio_desc *gpio_sync_in; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[2]; + } data ____cacheline_aligned; +}; + +static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr, + unsigned int len) +{ + unsigned int shift; + int ret; + + shift = 32 - (8 * len); + st->data.d8[0] = AD7768_RD_FLAG_MSK(addr); + + ret = spi_write_then_read(st->spi, st->data.d8, 1, + &st->data.d32, len); + if (ret < 0) + return ret; + + return (be32_to_cpu(st->data.d32) >> shift); +} + +static int ad7768_spi_reg_write(struct ad7768_state *st, + unsigned int addr, + unsigned int val) +{ + st->data.d8[0] = AD7768_WR_FLAG_MSK(addr); + st->data.d8[1] = val & 0xFF; + + return spi_write(st->spi, st->data.d8, 2); +} + +static int ad7768_set_mode(struct ad7768_state *st, + enum ad7768_conv_mode mode) +{ + int regval; + + regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1); + if (regval < 0) + return regval; + + regval &= ~AD7768_CONV_MODE_MSK; + regval |= AD7768_CONV_MODE(mode); + + return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval); +} + +static int ad7768_scan_direct(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int readval, ret; + + reinit_completion(&st->completion); + + ret = ad7768_set_mode(st, AD7768_ONE_SHOT); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) + return -ETIMEDOUT; + + readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); + if (readval < 0) + return readval; + /* + * Any SPI configuration of the AD7768-1 can only be + * performed in continuous conversion mode. + */ + ret = ad7768_set_mode(st, AD7768_CONTINUOUS); + if (ret < 0) + return ret; + + return readval; +} + +static int ad7768_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + if (readval) { + ret = ad7768_spi_reg_read(st, reg, 1); + if (ret < 0) + goto err_unlock; + *readval = ret; + ret = 0; + } else { + ret = ad7768_spi_reg_write(st, reg, writeval); + } +err_unlock: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7768_set_dig_fil(struct ad7768_state *st, + enum ad7768_dec_rate dec_rate) +{ + unsigned int mode; + int ret; + + if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16) + mode = AD7768_DIG_FIL_FIL(dec_rate); + else + mode = AD7768_DIG_FIL_DEC_RATE(dec_rate); + + ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode); + if (ret < 0) + return ret; + + /* A sync-in pulse is required every time the filter dec rate changes */ + gpiod_set_value(st->gpio_sync_in, 1); + gpiod_set_value(st->gpio_sync_in, 0); + + return 0; +} + +static int ad7768_set_freq(struct ad7768_state *st, + unsigned int freq) +{ + unsigned int diff_new, diff_old, pwr_mode, i, idx; + int res, ret; + + diff_old = U32_MAX; + idx = 0; + + res = DIV_ROUND_CLOSEST(st->mclk_freq, freq); + + /* Find the closest match for the desired sampling frequency */ + for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { + diff_new = abs(res - ad7768_clk_config[i].clk_div); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + /* + * Set both the mclk_div and pwrmode with a single write to the + * POWER_CLOCK register + */ + pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) | + AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode); + ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode); + if (ret < 0) + return ret; + + ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate); + if (ret < 0) + return ret; + + st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq, + ad7768_clk_config[idx].clk_div); + + return 0; +} + +static ssize_t ad7768_sampling_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int freq; + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { + freq = DIV_ROUND_CLOSEST(st->mclk_freq, + ad7768_clk_config[i].clk_div); + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq); + } + + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail); + +static int ad7768_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int scale_uv, ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ad7768_scan_direct(indio_dev); + if (ret >= 0) + *val = ret; + + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + scale_uv = regulator_get_voltage(st->vref); + if (scale_uv < 0) + return scale_uv; + + *val = (scale_uv * 2) / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->samp_freq; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad7768_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7768_set_freq(st, val); + default: + return -EINVAL; + } +} + +static struct attribute *ad7768_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7768_group = { + .attrs = ad7768_attributes, +}; + +static const struct iio_info ad7768_info = { + .attrs = &ad7768_group, + .read_raw = &ad7768_read_raw, + .write_raw = &ad7768_write_raw, + .debugfs_reg_access = &ad7768_reg_access, +}; + +static int ad7768_setup(struct ad7768_state *st) +{ + int ret; + + /* + * Two writes to the SPI_RESET[1:0] bits are required to initiate + * a software reset. The bits must first be set to 11, and then + * to 10. When the sequence is detected, the reset occurs. + * See the datasheet, page 70. + */ + ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3); + if (ret) + return ret; + + ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2); + if (ret) + return ret; + + st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_sync_in)) + return PTR_ERR(st->gpio_sync_in); + + /* Set the default sampling frequency to 32000 kSPS */ + return ad7768_set_freq(st, 32000); +} + +static irqreturn_t ad7768_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = spi_read(st->spi, &st->data.d32, 3); + if (ret < 0) + goto err_unlock; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->data.d32, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); +err_unlock: + mutex_unlock(&st->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t ad7768_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad7768_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad7768_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + iio_triggered_buffer_postenable(indio_dev); + /* + * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter + * continuous read mode. Subsequent data reads do not require an + * initial 8-bit write to query the ADC_DATA register. + */ + return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01); +} + +static int ad7768_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); + if (ret < 0) + return ret; + + return iio_triggered_buffer_predisable(indio_dev); +} + +static const struct iio_buffer_setup_ops ad7768_buffer_ops = { + .postenable = &ad7768_buffer_postenable, + .predisable = &ad7768_buffer_predisable, +}; + +static const struct iio_trigger_ops ad7768_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static void ad7768_regulator_disable(void *data) +{ + struct ad7768_state *st = data; + + regulator_disable(st->vref); +} + +static void ad7768_clk_disable(void *data) +{ + struct ad7768_state *st = data; + + clk_disable_unprepare(st->mclk); +} + +static int ad7768_probe(struct spi_device *spi) +{ + struct ad7768_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->vref = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->vref)) + return PTR_ERR(st->vref); + + ret = regulator_enable(st->vref); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified vref supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st); + if (ret) + return ret; + + st->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); + + ret = clk_prepare_enable(st->mclk); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st); + if (ret) + return ret; + + st->mclk_freq = clk_get_rate(st->mclk); + + spi_set_drvdata(spi, indio_dev); + mutex_init(&st->lock); + + indio_dev->channels = ad7768_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7768_channels); + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad7768_info; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED; + + ret = ad7768_setup(st); + if (ret < 0) { + dev_err(&spi->dev, "AD7768 setup failed\n"); + return ret; + } + + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7768_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&spi->dev, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + init_completion(&st->completion); + + ret = devm_request_irq(&spi->dev, spi->irq, + &ad7768_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &ad7768_trigger_handler, + &ad7768_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7768_id_table[] = { + { "ad7768-1", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7768_id_table); + +static const struct of_device_id ad7768_of_match[] = { + { .compatible = "adi,ad7768-1" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7768_of_match); + +static struct spi_driver ad7768_driver = { + .driver = { + .name = "ad7768-1", + .of_match_table = ad7768_of_match, + }, + .probe = ad7768_probe, + .id_table = ad7768_id_table, +}; +module_spi_driver(ad7768_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c @@ -115,6 +115,7 @@ #define MAX_ADC_V2_CHANNELS 10 #define MAX_ADC_V1_CHANNELS 8 #define MAX_EXYNOS3250_ADC_CHANNELS 2 +#define MAX_EXYNOS4212_ADC_CHANNELS 4 #define MAX_S5PV210_ADC_CHANNELS 10 /* Bit definitions common for ADC_V1 and ADC_V2 */ @@ -271,6 +272,19 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info, writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); } +/* Exynos4212 and 4412 is like ADCv1 but with four channels only */ +static const struct exynos_adc_data exynos4212_adc_data = { + .num_channels = MAX_EXYNOS4212_ADC_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV1_PHY_OFFSET, + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .clear_irq = exynos_adc_v1_clear_irq, + .start_conv = exynos_adc_v1_start_conv, +}; + static const struct exynos_adc_data exynos_adc_v1_data = { .num_channels = MAX_ADC_V1_CHANNELS, .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ @@ -493,6 +507,9 @@ static const struct of_device_id exynos_adc_match[] = { .compatible = "samsung,s5pv210-adc", .data = &exynos_adc_s5pv210_data, }, { + .compatible = "samsung,exynos4212-adc", + .data = &exynos4212_adc_data, + }, { .compatible = "samsung,exynos-adc-v1", .data = &exynos_adc_v1_data, }, { @@ -929,7 +946,7 @@ static int exynos_adc_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct exynos_adc *info = iio_priv(indio_dev); - if (IS_REACHABLE(CONFIG_INPUT)) { + if (IS_REACHABLE(CONFIG_INPUT) && info->input) { free_irq(info->tsirq, info); input_unregister_device(info->input); } diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for the Ingenic JZ47xx SoCs + * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu> + * + * based on drivers/mfd/jz4740-adc.c + */ + +#include <dt-bindings/iio/adc/ingenic,adc.h> +#include <linux/clk.h> +#include <linux/iio/iio.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#define JZ_ADC_REG_ENABLE 0x00 +#define JZ_ADC_REG_CFG 0x04 +#define JZ_ADC_REG_CTRL 0x08 +#define JZ_ADC_REG_STATUS 0x0c +#define JZ_ADC_REG_ADTCH 0x18 +#define JZ_ADC_REG_ADBDAT 0x1c +#define JZ_ADC_REG_ADSDAT 0x20 + +#define JZ_ADC_REG_CFG_BAT_MD BIT(4) + +#define JZ_ADC_AUX_VREF 3300 +#define JZ_ADC_AUX_VREF_BITS 12 +#define JZ_ADC_BATTERY_LOW_VREF 2500 +#define JZ_ADC_BATTERY_LOW_VREF_BITS 12 +#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500 +#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 +#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) +#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 + +struct ingenic_adc_soc_data { + unsigned int battery_high_vref; + unsigned int battery_high_vref_bits; + const int *battery_raw_avail; + size_t battery_raw_avail_size; + const int *battery_scale_avail; + size_t battery_scale_avail_size; +}; + +struct ingenic_adc { + void __iomem *base; + struct clk *clk; + struct mutex lock; + const struct ingenic_adc_soc_data *soc_data; + bool low_vref_mode; +}; + +static void ingenic_adc_set_config(struct ingenic_adc *adc, + uint32_t mask, + uint32_t val) +{ + uint32_t cfg; + + clk_enable(adc->clk); + mutex_lock(&adc->lock); + + cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; + cfg |= val; + writel(cfg, adc->base + JZ_ADC_REG_CFG); + + mutex_unlock(&adc->lock); + clk_disable(adc->clk); +} + +static void ingenic_adc_enable(struct ingenic_adc *adc, + int engine, + bool enabled) +{ + u8 val; + + mutex_lock(&adc->lock); + val = readb(adc->base + JZ_ADC_REG_ENABLE); + + if (enabled) + val |= BIT(engine); + else + val &= ~BIT(engine); + + writeb(val, adc->base + JZ_ADC_REG_ENABLE); + mutex_unlock(&adc->lock); +} + +static int ingenic_adc_capture(struct ingenic_adc *adc, + int engine) +{ + u8 val; + int ret; + + ingenic_adc_enable(adc, engine, true); + ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, + !(val & BIT(engine)), 250, 1000); + if (ret) + ingenic_adc_enable(adc, engine, false); + + return ret; +} + +static int ingenic_adc_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_BATTERY: + if (val > JZ_ADC_BATTERY_LOW_VREF) { + ingenic_adc_set_config(adc, + JZ_ADC_REG_CFG_BAT_MD, + 0); + adc->low_vref_mode = false; + } else { + ingenic_adc_set_config(adc, + JZ_ADC_REG_CFG_BAT_MD, + JZ_ADC_REG_CFG_BAT_MD); + adc->low_vref_mode = true; + } + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const int jz4725b_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4725b_adc_battery_scale_avail[] = { + JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const int jz4740_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4740_adc_battery_scale_avail[] = { + JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { + .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF, + .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + .battery_raw_avail = jz4725b_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), + .battery_scale_avail = jz4725b_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), +}; + +static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { + .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF, + .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + .battery_raw_avail = jz4740_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), + .battery_scale_avail = jz4740_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), +}; + +static int ingenic_adc_read_avail(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, + int *length, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + *type = IIO_VAL_INT; + *length = adc->soc_data->battery_raw_avail_size; + *vals = adc->soc_data->battery_raw_avail; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *length = adc->soc_data->battery_scale_avail_size; + *vals = adc->soc_data->battery_scale_avail; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + }; +} + +static int ingenic_adc_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + clk_enable(adc->clk); + ret = ingenic_adc_capture(adc, chan->channel); + if (ret) { + clk_disable(adc->clk); + return ret; + } + + switch (chan->channel) { + case INGENIC_ADC_AUX: + *val = readw(adc->base + JZ_ADC_REG_ADSDAT); + break; + case INGENIC_ADC_BATTERY: + *val = readw(adc->base + JZ_ADC_REG_ADBDAT); + break; + } + + clk_disable(adc->clk); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_AUX: + *val = JZ_ADC_AUX_VREF; + *val2 = JZ_ADC_AUX_VREF_BITS; + break; + case INGENIC_ADC_BATTERY: + if (adc->low_vref_mode) { + *val = JZ_ADC_BATTERY_LOW_VREF; + *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS; + } else { + *val = adc->soc_data->battery_high_vref; + *val2 = adc->soc_data->battery_high_vref_bits; + } + break; + } + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static void ingenic_adc_clk_cleanup(void *data) +{ + clk_unprepare(data); +} + +static const struct iio_info ingenic_adc_info = { + .write_raw = ingenic_adc_write_raw, + .read_raw = ingenic_adc_read_raw, + .read_avail = ingenic_adc_read_avail, +}; + +static const struct iio_chan_spec ingenic_channels[] = { + { + .extend_name = "aux", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX, + }, + { + .extend_name = "battery", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_BATTERY, + }, +}; + +static int ingenic_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio_dev; + struct ingenic_adc *adc; + struct resource *mem_base; + const struct ingenic_adc_soc_data *soc_data; + int ret; + + soc_data = device_get_match_data(dev); + if (!soc_data) + return -EINVAL; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio_dev) + return -ENOMEM; + + adc = iio_priv(iio_dev); + mutex_init(&adc->lock); + adc->soc_data = soc_data; + + mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adc->base = devm_ioremap_resource(dev, mem_base); + if (IS_ERR(adc->base)) { + dev_err(dev, "Unable to ioremap mmio resource\n"); + return PTR_ERR(adc->base); + } + + adc->clk = devm_clk_get(dev, "adc"); + if (IS_ERR(adc->clk)) { + dev_err(dev, "Unable to get clock\n"); + return PTR_ERR(adc->clk); + } + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(dev, "Failed to enable clock\n"); + return ret; + } + + /* Put hardware in a known passive state. */ + writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); + writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + clk_disable(adc->clk); + + ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); + if (ret) { + dev_err(dev, "Unable to add action\n"); + return ret; + } + + iio_dev->dev.parent = dev; + iio_dev->name = "jz-adc"; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = ingenic_channels; + iio_dev->num_channels = ARRAY_SIZE(ingenic_channels); + iio_dev->info = &ingenic_adc_info; + + ret = devm_iio_device_register(dev, iio_dev); + if (ret) + dev_err(dev, "Unable to register IIO device\n"); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id ingenic_adc_of_match[] = { + { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, + { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, + { }, +}; +MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); +#endif + +static struct platform_driver ingenic_adc_driver = { + .driver = { + .name = "ingenic-adc", + .of_match_table = of_match_ptr(ingenic_adc_of_match), + }, + .probe = ingenic_adc_probe, +}; +module_platform_driver(ingenic_adc_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * lpc32xx_adc.c - Support for ADC in LPC32XX * * 3-channel, 10-bit ADC * * Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de> - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c @@ -26,6 +26,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/mfd/syscon.h> #define MESON_SAR_ADC_REG0 0x00 #define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31) @@ -174,6 +175,9 @@ #define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0) #define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7) +#define MESON_HHI_DPLL_TOP_0 0x318 +#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9) + /* for use with IIO_VAL_INT_PLUS_MICRO */ #define MILLION 1000000 @@ -280,6 +284,7 @@ struct meson_sar_adc_priv { struct completion done; int calibbias; int calibscale; + struct regmap *tsc_regmap; bool temperature_sensor_calibrated; u8 temperature_sensor_coefficient; u16 temperature_sensor_adc_val; @@ -727,6 +732,15 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) return ret; } + priv->tsc_regmap = + syscon_regmap_lookup_by_phandle(indio_dev->dev.parent->of_node, + "amlogic,hhi-sysctrl"); + if (IS_ERR(priv->tsc_regmap)) { + dev_err(indio_dev->dev.parent, + "failed to get amlogic,hhi-sysctrl regmap\n"); + return PTR_ERR(priv->tsc_regmap); + } + read_len = MESON_SAR_ADC_EFUSE_BYTES; buf = nvmem_cell_read(temperature_calib, &read_len); if (IS_ERR(buf)) { @@ -861,6 +875,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) priv->temperature_sensor_coefficient); regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval); + + if (priv->param->temperature_trimming_bits == 5) { + if (priv->temperature_sensor_coefficient & BIT(4)) + regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4; + else + regval = 0; + + /* + * bit [4] (the 5th bit when starting to count at 1) + * of the TSC is located in the HHI register area. + */ + regmap_update_bits(priv->tsc_regmap, + MESON_HHI_DPLL_TOP_0, + MESON_HHI_DPLL_TOP_0_TSC_BIT4, + regval); + } } else { regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, MESON_SAR_ADC_DELTA_10_TS_REVE1, 0); @@ -1064,6 +1094,9 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, + .temperature_trimming_bits = 5, + .temperature_multiplier = 10, + .temperature_divider = 32, }; static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Nuvoton Technology corporation. + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/mfd/syscon.h> +#include <linux/io.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> + +struct npcm_adc { + bool int_status; + u32 adc_sample_hz; + struct device *dev; + void __iomem *regs; + struct clk *adc_clk; + wait_queue_head_t wq; + struct regulator *vref; + struct regmap *rst_regmap; +}; + +/* NPCM7xx reset module */ +#define NPCM7XX_IPSRST1_OFFSET 0x020 +#define NPCM7XX_IPSRST1_ADC_RST BIT(27) + +/* ADC registers */ +#define NPCM_ADCCON 0x00 +#define NPCM_ADCDATA 0x04 + +/* ADCCON Register Bits */ +#define NPCM_ADCCON_ADC_INT_EN BIT(21) +#define NPCM_ADCCON_REFSEL BIT(19) +#define NPCM_ADCCON_ADC_INT_ST BIT(18) +#define NPCM_ADCCON_ADC_EN BIT(17) +#define NPCM_ADCCON_ADC_RST BIT(16) +#define NPCM_ADCCON_ADC_CONV BIT(13) + +#define NPCM_ADCCON_CH_MASK GENMASK(27, 24) +#define NPCM_ADCCON_CH(x) ((x) << 24) +#define NPCM_ADCCON_DIV_SHIFT 1 +#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1) +#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0)) + +#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN) + +/* ADC General Definition */ +#define NPCM_RESOLUTION_BITS 10 +#define NPCM_INT_VREF_MV 2000 + +#define NPCM_ADC_CHAN(ch) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = ch, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec npcm_adc_iio_channels[] = { + NPCM_ADC_CHAN(0), + NPCM_ADC_CHAN(1), + NPCM_ADC_CHAN(2), + NPCM_ADC_CHAN(3), + NPCM_ADC_CHAN(4), + NPCM_ADC_CHAN(5), + NPCM_ADC_CHAN(6), + NPCM_ADC_CHAN(7), +}; + +static irqreturn_t npcm_adc_isr(int irq, void *data) +{ + u32 regtemp; + struct iio_dev *indio_dev = data; + struct npcm_adc *info = iio_priv(indio_dev); + + regtemp = ioread32(info->regs + NPCM_ADCCON); + if (regtemp & NPCM_ADCCON_ADC_INT_ST) { + iowrite32(regtemp, info->regs + NPCM_ADCCON); + wake_up_interruptible(&info->wq); + info->int_status = true; + } + + return IRQ_HANDLED; +} + +static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel) +{ + int ret; + u32 regtemp; + + /* Select ADC channel */ + regtemp = ioread32(info->regs + NPCM_ADCCON); + regtemp &= ~NPCM_ADCCON_CH_MASK; + info->int_status = false; + iowrite32(regtemp | NPCM_ADCCON_CH(channel) | + NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); + + ret = wait_event_interruptible_timeout(info->wq, info->int_status, + msecs_to_jiffies(10)); + if (ret == 0) { + regtemp = ioread32(info->regs + NPCM_ADCCON); + if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) { + /* if conversion failed - reset ADC module */ + regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, + NPCM7XX_IPSRST1_ADC_RST); + msleep(100); + regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, + 0x0); + msleep(100); + + /* Enable ADC and start conversion module */ + iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV, + info->regs + NPCM_ADCCON); + dev_err(info->dev, "RESET ADC Complete\n"); + } + return -ETIMEDOUT; + } + if (ret < 0) + return ret; + + *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA)); + + return 0; +} + +static int npcm_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + int vref_uv; + struct npcm_adc *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = npcm_adc_read(info, val, chan->channel); + mutex_unlock(&indio_dev->mlock); + if (ret) { + dev_err(info->dev, "NPCM ADC read failed\n"); + return ret; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (info->vref) { + vref_uv = regulator_get_voltage(info->vref); + *val = vref_uv / 1000; + } else { + *val = NPCM_INT_VREF_MV; + } + *val2 = NPCM_RESOLUTION_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->adc_sample_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info npcm_adc_iio_info = { + .read_raw = &npcm_adc_read_raw, +}; + +static const struct of_device_id npcm_adc_match[] = { + { .compatible = "nuvoton,npcm750-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, npcm_adc_match); + +static int npcm_adc_probe(struct platform_device *pdev) +{ + int ret; + int irq; + u32 div; + u32 reg_con; + struct resource *res; + struct npcm_adc *info; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + info = iio_priv(indio_dev); + + info->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + info->adc_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->adc_clk)) { + dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); + return PTR_ERR(info->adc_clk); + } + + /* calculate ADC clock sample rate */ + reg_con = ioread32(info->regs + NPCM_ADCCON); + div = reg_con & NPCM_ADCCON_DIV_MASK; + div = div >> NPCM_ADCCON_DIV_SHIFT; + info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); + + if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) { + info->rst_regmap = syscon_regmap_lookup_by_compatible + ("nuvoton,npcm750-rst"); + if (IS_ERR(info->rst_regmap)) { + dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n"); + ret = PTR_ERR(info->rst_regmap); + goto err_disable_clk; + } + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "failed getting interrupt resource\n"); + ret = -EINVAL; + goto err_disable_clk; + } + + ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, + "NPCM_ADC", indio_dev); + if (ret < 0) { + dev_err(dev, "failed requesting interrupt\n"); + goto err_disable_clk; + } + + reg_con = ioread32(info->regs + NPCM_ADCCON); + info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); + if (!IS_ERR(info->vref)) { + ret = regulator_enable(info->vref); + if (ret) { + dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); + goto err_disable_clk; + } + + iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, + info->regs + NPCM_ADCCON); + } else { + /* + * Any error which is not ENODEV indicates the regulator + * has been specified and so is a failure case. + */ + if (PTR_ERR(info->vref) != -ENODEV) { + ret = PTR_ERR(info->vref); + goto err_disable_clk; + } + + /* Use internal reference */ + iowrite32(reg_con | NPCM_ADCCON_REFSEL, + info->regs + NPCM_ADCCON); + } + + init_waitqueue_head(&info->wq); + + reg_con = ioread32(info->regs + NPCM_ADCCON); + reg_con |= NPCM_ADC_ENABLE; + + /* Enable the ADC Module */ + iowrite32(reg_con, info->regs + NPCM_ADCCON); + + /* Start ADC conversion */ + iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); + + platform_set_drvdata(pdev, indio_dev); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &npcm_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = npcm_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto err_iio_register; + } + + pr_info("NPCM ADC driver probed\n"); + + return 0; + +err_iio_register: + iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); + if (!IS_ERR(info->vref)) + regulator_disable(info->vref); +err_disable_clk: + clk_disable_unprepare(info->adc_clk); + + return ret; +} + +static int npcm_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct npcm_adc *info = iio_priv(indio_dev); + u32 regtemp; + + iio_device_unregister(indio_dev); + + regtemp = ioread32(info->regs + NPCM_ADCCON); + iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); + if (!IS_ERR(info->vref)) + regulator_disable(info->vref); + clk_disable_unprepare(info->adc_clk); + + return 0; +} + +static struct platform_driver npcm_adc_driver = { + .probe = npcm_adc_probe, + .remove = npcm_adc_remove, + .driver = { + .name = "npcm_adc", + .of_match_table = npcm_adc_match, + }, +}; + +module_platform_driver(npcm_adc_driver); + +MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver"); +MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* TI ADS124S0X chip family driver + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/gpio/consumer.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/sysfs.h> + +/* Commands */ +#define ADS124S08_CMD_NOP 0x00 +#define ADS124S08_CMD_WAKEUP 0x02 +#define ADS124S08_CMD_PWRDWN 0x04 +#define ADS124S08_CMD_RESET 0x06 +#define ADS124S08_CMD_START 0x08 +#define ADS124S08_CMD_STOP 0x0a +#define ADS124S08_CMD_SYOCAL 0x16 +#define ADS124S08_CMD_SYGCAL 0x17 +#define ADS124S08_CMD_SFOCAL 0x19 +#define ADS124S08_CMD_RDATA 0x12 +#define ADS124S08_CMD_RREG 0x20 +#define ADS124S08_CMD_WREG 0x40 + +/* Registers */ +#define ADS124S08_ID_REG 0x00 +#define ADS124S08_STATUS 0x01 +#define ADS124S08_INPUT_MUX 0x02 +#define ADS124S08_PGA 0x03 +#define ADS124S08_DATA_RATE 0x04 +#define ADS124S08_REF 0x05 +#define ADS124S08_IDACMAG 0x06 +#define ADS124S08_IDACMUX 0x07 +#define ADS124S08_VBIAS 0x08 +#define ADS124S08_SYS 0x09 +#define ADS124S08_OFCAL0 0x0a +#define ADS124S08_OFCAL1 0x0b +#define ADS124S08_OFCAL2 0x0c +#define ADS124S08_FSCAL0 0x0d +#define ADS124S08_FSCAL1 0x0e +#define ADS124S08_FSCAL2 0x0f +#define ADS124S08_GPIODAT 0x10 +#define ADS124S08_GPIOCON 0x11 + +/* ADS124S0x common channels */ +#define ADS124S08_AIN0 0x00 +#define ADS124S08_AIN1 0x01 +#define ADS124S08_AIN2 0x02 +#define ADS124S08_AIN3 0x03 +#define ADS124S08_AIN4 0x04 +#define ADS124S08_AIN5 0x05 +#define ADS124S08_AINCOM 0x0c +/* ADS124S08 only channels */ +#define ADS124S08_AIN6 0x06 +#define ADS124S08_AIN7 0x07 +#define ADS124S08_AIN8 0x08 +#define ADS124S08_AIN9 0x09 +#define ADS124S08_AIN10 0x0a +#define ADS124S08_AIN11 0x0b +#define ADS124S08_MAX_CHANNELS 12 + +#define ADS124S08_POS_MUX_SHIFT 0x04 +#define ADS124S08_INT_REF 0x09 + +#define ADS124S08_START_REG_MASK 0x1f +#define ADS124S08_NUM_BYTES_MASK 0x1f + +#define ADS124S08_START_CONV 0x01 +#define ADS124S08_STOP_CONV 0x00 + +enum ads124s_id { + ADS124S08_ID, + ADS124S06_ID, +}; + +struct ads124s_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ads124s_private { + const struct ads124s_chip_info *chip_info; + struct gpio_desc *reset_gpio; + struct spi_device *spi; + struct mutex lock; + u8 data[5] ____cacheline_aligned; +}; + +#define ADS124S08_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + }, \ +} + +static const struct iio_chan_spec ads124s06_channels[] = { + ADS124S08_CHAN(0), + ADS124S08_CHAN(1), + ADS124S08_CHAN(2), + ADS124S08_CHAN(3), + ADS124S08_CHAN(4), + ADS124S08_CHAN(5), +}; + +static const struct iio_chan_spec ads124s08_channels[] = { + ADS124S08_CHAN(0), + ADS124S08_CHAN(1), + ADS124S08_CHAN(2), + ADS124S08_CHAN(3), + ADS124S08_CHAN(4), + ADS124S08_CHAN(5), + ADS124S08_CHAN(6), + ADS124S08_CHAN(7), + ADS124S08_CHAN(8), + ADS124S08_CHAN(9), + ADS124S08_CHAN(10), + ADS124S08_CHAN(11), +}; + +static const struct ads124s_chip_info ads124s_chip_info_tbl[] = { + [ADS124S08_ID] = { + .channels = ads124s08_channels, + .num_channels = ARRAY_SIZE(ads124s08_channels), + }, + [ADS124S06_ID] = { + .channels = ads124s06_channels, + .num_channels = ARRAY_SIZE(ads124s06_channels), + }, +}; + +static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + priv->data[0] = command; + + return spi_write(priv->spi, &priv->data[0], 1); +} + +static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + priv->data[0] = ADS124S08_CMD_WREG | reg; + priv->data[1] = 0x0; + priv->data[2] = data; + + return spi_write(priv->spi, &priv->data[0], 3); +} + +static int ads124s_reset(struct iio_dev *indio_dev) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + if (priv->reset_gpio) { + gpiod_set_value(priv->reset_gpio, 0); + udelay(200); + gpiod_set_value(priv->reset_gpio, 1); + } else { + return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET); + } + + return 0; +}; + +static int ads124s_read(struct iio_dev *indio_dev, unsigned int chan) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + int ret; + u32 tmp; + struct spi_transfer t[] = { + { + .tx_buf = &priv->data[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &priv->data[1], + .rx_buf = &priv->data[1], + .len = 4, + }, + }; + + priv->data[0] = ADS124S08_CMD_RDATA; + memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data)); + + ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + tmp = priv->data[2] << 16 | priv->data[3] << 8 | priv->data[4]; + + return tmp; +} + +static int ads124s_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + int ret; + + mutex_lock(&priv->lock); + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, + chan->channel); + if (ret) { + dev_err(&priv->spi->dev, "Set ADC CH failed\n"); + goto out; + } + + ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); + if (ret) { + dev_err(&priv->spi->dev, "Start conversions failed\n"); + goto out; + } + + ret = ads124s_read(indio_dev, chan->channel); + if (ret < 0) { + dev_err(&priv->spi->dev, "Read ADC failed\n"); + goto out; + } + + *val = ret; + + ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); + if (ret) { + dev_err(&priv->spi->dev, "Stop conversions failed\n"); + goto out; + } + + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } +out: + mutex_unlock(&priv->lock); + return ret; +} + +static const struct iio_info ads124s_info = { + .read_raw = &ads124s_read_raw, +}; + +static irqreturn_t ads124s_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads124s_private *priv = iio_priv(indio_dev); + u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u16)]; + int scan_index, j = 0; + int ret; + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, + scan_index); + if (ret) + dev_err(&priv->spi->dev, "Set ADC CH failed\n"); + + ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); + if (ret) + dev_err(&priv->spi->dev, "Start ADC conversions failed\n"); + + buffer[j] = ads124s_read(indio_dev, scan_index); + ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); + if (ret) + dev_err(&priv->spi->dev, "Stop ADC conversions failed\n"); + + j++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads124s_probe(struct spi_device *spi) +{ + struct ads124s_private *ads124s_priv; + struct iio_dev *indio_dev; + const struct spi_device_id *spi_id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv)); + if (indio_dev == NULL) + return -ENOMEM; + + ads124s_priv = iio_priv(indio_dev); + + ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(ads124s_priv->reset_gpio)) + dev_info(&spi->dev, "Reset GPIO not defined\n"); + + ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; + + spi_set_drvdata(spi, indio_dev); + + ads124s_priv->spi = spi; + + indio_dev->name = spi_id->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads124s_priv->chip_info->channels; + indio_dev->num_channels = ads124s_priv->chip_info->num_channels; + indio_dev->info = &ads124s_info; + + mutex_init(&ads124s_priv->lock); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ads124s_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + return ret; + } + + ads124s_reset(indio_dev); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ads124s_id[] = { + { "ads124s06", ADS124S06_ID }, + { "ads124s08", ADS124S08_ID }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads124s_id); + +static const struct of_device_id ads124s_of_table[] = { + { .compatible = "ti,ads124s06" }, + { .compatible = "ti,ads124s08" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ads124s_of_table); + +static struct spi_driver ads124s_driver = { + .driver = { + .name = "ads124s08", + .of_match_table = ads124s_of_table, + }, + .probe = ads124s_probe, + .id_table = ads124s_id, +}; +module_spi_driver(ads124s_driver); + +MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>"); +MODULE_DESCRIPTION("TI TI_ADS12S0X ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c @@ -1273,8 +1273,10 @@ static int xadc_probe(struct platform_device *pdev) xadc->threshold[i] = 0xffff; else xadc->threshold[i] = 0; - xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i), + ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i), xadc->threshold[i]); + if (ret) + goto err_free_irq; } /* Go to non-buffered mode */ diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig @@ -61,6 +61,27 @@ config IAQCORE iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds) sensors +config PMS7003 + tristate "Plantower PMS7003 particulate matter sensor" + depends on SERIAL_DEV_BUS + help + Say Y here to build support for the Plantower PMS7003 particulate + matter sensor. + + To compile this driver as a module, choose M here: the module will + be called pms7003. + +config SPS30 + tristate "SPS30 particulate matter sensor" + depends on I2C + select CRC8 + help + Say Y here to build support for the Sensirion SPS30 particulate + matter sensor. + + To compile this driver as a module, choose M here: the module will + be called sps30. + config VZ89X tristate "SGX Sensortech MiCS VZ89X VOC sensor" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile @@ -9,4 +9,7 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o obj-$(CONFIG_BME680_SPI) += bme680_spi.o obj-$(CONFIG_CCS811) += ccs811.o obj-$(CONFIG_IAQCORE) += ams-iaq-core.o +obj-$(CONFIG_PMS7003) += pms7003.o +obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o +obj-$(CONFIG_SPS30) += sps30.o obj-$(CONFIG_VZ89X) += vz89x.o diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c @@ -70,10 +70,17 @@ static const struct acpi_device_id bme680_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, bme680_acpi_match); +static const struct of_device_id bme680_of_i2c_match[] = { + { .compatible = "bosch,bme680", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bme680_of_i2c_match); + static struct i2c_driver bme680_i2c_driver = { .driver = { .name = "bme680_i2c", .acpi_match_table = ACPI_PTR(bme680_acpi_match), + .of_match_table = bme680_of_i2c_match, }, .probe = bme680_i2c_probe, .id_table = bme680_i2c_id, diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c @@ -6,6 +6,7 @@ */ #include <linux/acpi.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/regmap.h> #include <linux/spi/spi.h> @@ -110,10 +111,17 @@ static const struct acpi_device_id bme680_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, bme680_acpi_match); +static const struct of_device_id bme680_of_spi_match[] = { + { .compatible = "bosch,bme680", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bme680_of_spi_match); + static struct spi_driver bme680_spi_driver = { .driver = { .name = "bme680_spi", .acpi_match_table = ACPI_PTR(bme680_acpi_match), + .of_match_table = bme680_of_spi_match, }, .probe = bme680_spi_probe, .id_table = bme680_spi_id, diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Plantower PMS7003 particulate matter sensor driver + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + */ + +#include <asm/unaligned.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/serdev.h> + +#define PMS7003_DRIVER_NAME "pms7003" + +#define PMS7003_MAGIC 0x424d +/* last 2 data bytes hold frame checksum */ +#define PMS7003_MAX_DATA_LENGTH 28 +#define PMS7003_CHECKSUM_LENGTH 2 +#define PMS7003_PM10_OFFSET 10 +#define PMS7003_PM2P5_OFFSET 8 +#define PMS7003_PM1_OFFSET 6 + +#define PMS7003_TIMEOUT msecs_to_jiffies(6000) +#define PMS7003_CMD_LENGTH 7 +#define PMS7003_PM_MAX 1000 +#define PMS7003_PM_MIN 0 + +enum { + PM1, + PM2P5, + PM10, +}; + +enum pms7003_cmd { + CMD_WAKEUP, + CMD_ENTER_PASSIVE_MODE, + CMD_READ_PASSIVE, + CMD_SLEEP, +}; + +/* + * commands have following format: + * + * +------+------+-----+------+-----+-----------+-----------+ + * | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb | + * +------+------+-----+------+-----+-----------+-----------+ + */ +static const u8 pms7003_cmd_tbl[][PMS7003_CMD_LENGTH] = { + [CMD_WAKEUP] = { 0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74 }, + [CMD_ENTER_PASSIVE_MODE] = { 0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70 }, + [CMD_READ_PASSIVE] = { 0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71 }, + [CMD_SLEEP] = { 0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73 }, +}; + +struct pms7003_frame { + u8 data[PMS7003_MAX_DATA_LENGTH]; + u16 expected_length; + u16 length; +}; + +struct pms7003_state { + struct serdev_device *serdev; + struct pms7003_frame frame; + struct completion frame_ready; + struct mutex lock; /* must be held whenever state gets touched */ +}; + +static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd) +{ + int ret; + + ret = serdev_device_write(state->serdev, pms7003_cmd_tbl[cmd], + PMS7003_CMD_LENGTH, PMS7003_TIMEOUT); + if (ret < PMS7003_CMD_LENGTH) + return ret < 0 ? ret : -EIO; + + ret = wait_for_completion_interruptible_timeout(&state->frame_ready, + PMS7003_TIMEOUT); + if (!ret) + ret = -ETIMEDOUT; + + return ret < 0 ? ret : 0; +} + +static u16 pms7003_get_pm(const u8 *data) +{ + return clamp_val(get_unaligned_be16(data), + PMS7003_PM_MIN, PMS7003_PM_MAX); +} + +static irqreturn_t pms7003_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct pms7003_state *state = iio_priv(indio_dev); + struct pms7003_frame *frame = &state->frame; + u16 data[3 + 1 + 4]; /* PM1, PM2P5, PM10, padding, timestamp */ + int ret; + + mutex_lock(&state->lock); + ret = pms7003_do_cmd(state, CMD_READ_PASSIVE); + if (ret) { + mutex_unlock(&state->lock); + goto err; + } + + data[PM1] = pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET); + data[PM2P5] = pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET); + data[PM10] = pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET); + mutex_unlock(&state->lock); + + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int pms7003_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct pms7003_state *state = iio_priv(indio_dev); + struct pms7003_frame *frame = &state->frame; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_MASSCONCENTRATION: + mutex_lock(&state->lock); + ret = pms7003_do_cmd(state, CMD_READ_PASSIVE); + if (ret) { + mutex_unlock(&state->lock); + return ret; + } + + *val = pms7003_get_pm(frame->data + chan->address); + mutex_unlock(&state->lock); + + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static const struct iio_info pms7003_info = { + .read_raw = pms7003_read_raw, +}; + +#define PMS7003_CHAN(_index, _mod, _addr) { \ + .type = IIO_MASSCONCENTRATION, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _mod, \ + .address = _addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec pms7003_channels[] = { + PMS7003_CHAN(0, PM1, PMS7003_PM1_OFFSET), + PMS7003_CHAN(1, PM2P5, PMS7003_PM2P5_OFFSET), + PMS7003_CHAN(2, PM10, PMS7003_PM10_OFFSET), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static u16 pms7003_calc_checksum(struct pms7003_frame *frame) +{ + u16 checksum = (PMS7003_MAGIC >> 8) + (u8)(PMS7003_MAGIC & 0xff) + + (frame->length >> 8) + (u8)frame->length; + int i; + + for (i = 0; i < frame->length - PMS7003_CHECKSUM_LENGTH; i++) + checksum += frame->data[i]; + + return checksum; +} + +static bool pms7003_frame_is_okay(struct pms7003_frame *frame) +{ + int offset = frame->length - PMS7003_CHECKSUM_LENGTH; + u16 checksum = get_unaligned_be16(frame->data + offset); + + return checksum == pms7003_calc_checksum(frame); +} + +static int pms7003_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); + struct pms7003_state *state = iio_priv(indio_dev); + struct pms7003_frame *frame = &state->frame; + int num; + + if (!frame->expected_length) { + u16 magic; + + /* wait for SOF and data length */ + if (size < 4) + return 0; + + magic = get_unaligned_be16(buf); + if (magic != PMS7003_MAGIC) + return 2; + + num = get_unaligned_be16(buf + 2); + if (num <= PMS7003_MAX_DATA_LENGTH) { + frame->expected_length = num; + frame->length = 0; + } + + return 4; + } + + num = min(size, (size_t)(frame->expected_length - frame->length)); + memcpy(frame->data + frame->length, buf, num); + frame->length += num; + + if (frame->length == frame->expected_length) { + if (pms7003_frame_is_okay(frame)) + complete(&state->frame_ready); + + frame->expected_length = 0; + } + + return num; +} + +static const struct serdev_device_ops pms7003_serdev_ops = { + .receive_buf = pms7003_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static void pms7003_stop(void *data) +{ + struct pms7003_state *state = data; + + pms7003_do_cmd(state, CMD_SLEEP); +} + +static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 }; + +static int pms7003_probe(struct serdev_device *serdev) +{ + struct pms7003_state *state; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + serdev_device_set_drvdata(serdev, indio_dev); + state->serdev = serdev; + indio_dev->dev.parent = &serdev->dev; + indio_dev->info = &pms7003_info; + indio_dev->name = PMS7003_DRIVER_NAME; + indio_dev->channels = pms7003_channels, + indio_dev->num_channels = ARRAY_SIZE(pms7003_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = pms7003_scan_masks; + + mutex_init(&state->lock); + init_completion(&state->frame_ready); + + serdev_device_set_client_ops(serdev, &pms7003_serdev_ops); + ret = devm_serdev_device_open(&serdev->dev, serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, 9600); + serdev_device_set_flow_control(serdev, false); + + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) + return ret; + + ret = pms7003_do_cmd(state, CMD_WAKEUP); + if (ret) { + dev_err(&serdev->dev, "failed to wakeup sensor\n"); + return ret; + } + + ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE); + if (ret) { + dev_err(&serdev->dev, "failed to enter passive mode\n"); + return ret; + } + + ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL, + pms7003_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&serdev->dev, indio_dev); +} + +static const struct of_device_id pms7003_of_match[] = { + { .compatible = "plantower,pms7003" }, + { } +}; +MODULE_DEVICE_TABLE(of, pms7003_of_match); + +static struct serdev_device_driver pms7003_driver = { + .driver = { + .name = PMS7003_DRIVER_NAME, + .of_match_table = pms7003_of_match, + }, + .probe = pms7003_probe, +}; +module_serdev_device_driver(pms7003_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); +MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sgp30.c - Support for Sensirion SGP Gas Sensors + * + * Copyright (C) 2018 Andreas Brauchli <andreas.brauchli@sensirion.com> + * + * I2C slave address: 0x58 + * + * Datasheets: + * https://www.sensirion.com/file/datasheet_sgp30 + * https://www.sensirion.com/file/datasheet_sgpc3 + * + * TODO: + * - baseline support + * - humidity compensation + * - power mode switching (SGPC3) + */ + +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define SGP_WORD_LEN 2 +#define SGP_CRC8_POLYNOMIAL 0x31 +#define SGP_CRC8_INIT 0xff +#define SGP_CRC8_LEN 1 +#define SGP_CMD(cmd_word) cpu_to_be16(cmd_word) +#define SGP_CMD_DURATION_US 12000 +#define SGP_MEASUREMENT_DURATION_US 50000 +#define SGP_CMD_LEN SGP_WORD_LEN +#define SGP_CMD_MAX_BUF_SIZE (SGP_CMD_LEN + 2 * SGP_WORD_LEN) +#define SGP_MEASUREMENT_LEN 2 +#define SGP30_MEASURE_INTERVAL_HZ 1 +#define SGPC3_MEASURE_INTERVAL_HZ 2 +#define SGP_VERS_PRODUCT(data) ((((data)->feature_set) & 0xf000) >> 12) +#define SGP_VERS_RESERVED(data) ((((data)->feature_set) & 0x0800) >> 11) +#define SGP_VERS_GEN(data) ((((data)->feature_set) & 0x0600) >> 9) +#define SGP_VERS_ENG_BIT(data) ((((data)->feature_set) & 0x0100) >> 8) +#define SGP_VERS_MAJOR(data) ((((data)->feature_set) & 0x00e0) >> 5) +#define SGP_VERS_MINOR(data) (((data)->feature_set) & 0x001f) + +DECLARE_CRC8_TABLE(sgp_crc8_table); + +enum sgp_product_id { + SGP30 = 0, + SGPC3, +}; + +enum sgp30_channel_idx { + SGP30_IAQ_TVOC_IDX = 0, + SGP30_IAQ_CO2EQ_IDX, + SGP30_SIG_ETOH_IDX, + SGP30_SIG_H2_IDX, +}; + +enum sgpc3_channel_idx { + SGPC3_IAQ_TVOC_IDX = 10, + SGPC3_SIG_ETOH_IDX, +}; + +enum sgp_cmd { + SGP_CMD_IAQ_INIT = SGP_CMD(0x2003), + SGP_CMD_IAQ_MEASURE = SGP_CMD(0x2008), + SGP_CMD_GET_FEATURE_SET = SGP_CMD(0x202f), + SGP_CMD_GET_SERIAL_ID = SGP_CMD(0x3682), + + SGP30_CMD_MEASURE_SIGNAL = SGP_CMD(0x2050), + + SGPC3_CMD_MEASURE_RAW = SGP_CMD(0x2046), +}; + +struct sgp_version { + u8 major; + u8 minor; +}; + +struct sgp_crc_word { + __be16 value; + u8 crc8; +} __attribute__((__packed__)); + +union sgp_reading { + u8 start; + struct sgp_crc_word raw_words[4]; +}; + +enum _iaq_buffer_state { + IAQ_BUFFER_EMPTY = 0, + IAQ_BUFFER_DEFAULT_VALS, + IAQ_BUFFER_VALID, +}; + +struct sgp_data { + struct i2c_client *client; + struct task_struct *iaq_thread; + struct mutex data_lock; + unsigned long iaq_init_start_jiffies; + unsigned long iaq_defval_skip_jiffies; + u16 product_id; + u16 feature_set; + unsigned long measure_interval_jiffies; + enum sgp_cmd iaq_init_cmd; + enum sgp_cmd measure_iaq_cmd; + enum sgp_cmd measure_gas_signals_cmd; + union sgp_reading buffer; + union sgp_reading iaq_buffer; + enum _iaq_buffer_state iaq_buffer_state; +}; + +struct sgp_device { + const struct iio_chan_spec *channels; + int num_channels; +}; + +static const struct sgp_version supported_versions_sgp30[] = { + { + .major = 1, + .minor = 0, + }, +}; + +static const struct sgp_version supported_versions_sgpc3[] = { + { + .major = 0, + .minor = 4, + }, +}; + +static const struct iio_chan_spec sgp30_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = SGP30_IAQ_TVOC_IDX, + }, + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = SGP30_IAQ_CO2EQ_IDX, + }, + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_ETHANOL, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = SGP30_SIG_ETOH_IDX, + }, + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_H2, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = SGP30_SIG_H2_IDX, + }, +}; + +static const struct iio_chan_spec sgpc3_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = SGPC3_IAQ_TVOC_IDX, + }, + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_ETHANOL, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = SGPC3_SIG_ETOH_IDX, + }, +}; + +static const struct sgp_device sgp_devices[] = { + [SGP30] = { + .channels = sgp30_channels, + .num_channels = ARRAY_SIZE(sgp30_channels), + }, + [SGPC3] = { + .channels = sgpc3_channels, + .num_channels = ARRAY_SIZE(sgpc3_channels), + }, +}; + +/** + * sgp_verify_buffer() - verify the checksums of the data buffer words + * + * @data: SGP data + * @buf: Raw data buffer + * @word_count: Num data words stored in the buffer, excluding CRC bytes + * + * Return: 0 on success, negative error otherwise. + */ +static int sgp_verify_buffer(const struct sgp_data *data, + union sgp_reading *buf, size_t word_count) +{ + size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN); + int i; + u8 crc; + u8 *data_buf = &buf->start; + + for (i = 0; i < size; i += SGP_WORD_LEN + SGP_CRC8_LEN) { + crc = crc8(sgp_crc8_table, &data_buf[i], SGP_WORD_LEN, + SGP_CRC8_INIT); + if (crc != data_buf[i + SGP_WORD_LEN]) { + dev_err(&data->client->dev, "CRC error\n"); + return -EIO; + } + } + + return 0; +} + +/** + * sgp_read_cmd() - reads data from sensor after issuing a command + * The caller must hold data->data_lock for the duration of the call. + * @data: SGP data + * @cmd: SGP Command to issue + * @buf: Raw data buffer to use + * @word_count: Num words to read, excluding CRC bytes + * + * Return: 0 on success, negative error otherwise. + */ +static int sgp_read_cmd(struct sgp_data *data, enum sgp_cmd cmd, + union sgp_reading *buf, size_t word_count, + unsigned long duration_us) +{ + int ret; + struct i2c_client *client = data->client; + size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN); + u8 *data_buf; + + ret = i2c_master_send(client, (const char *)&cmd, SGP_CMD_LEN); + if (ret != SGP_CMD_LEN) + return -EIO; + usleep_range(duration_us, duration_us + 1000); + + if (word_count == 0) + return 0; + + data_buf = &buf->start; + ret = i2c_master_recv(client, data_buf, size); + if (ret < 0) + return ret; + if (ret != size) + return -EIO; + + return sgp_verify_buffer(data, buf, word_count); +} + +/** + * sgp_measure_iaq() - measure and retrieve IAQ values from sensor + * The caller must hold data->data_lock for the duration of the call. + * @data: SGP data + * + * Return: 0 on success, -EBUSY on default values, negative error + * otherwise. + */ + +static int sgp_measure_iaq(struct sgp_data *data) +{ + int ret; + /* data contains default values */ + bool default_vals = !time_after(jiffies, data->iaq_init_start_jiffies + + data->iaq_defval_skip_jiffies); + + ret = sgp_read_cmd(data, data->measure_iaq_cmd, &data->iaq_buffer, + SGP_MEASUREMENT_LEN, SGP_MEASUREMENT_DURATION_US); + if (ret < 0) + return ret; + + data->iaq_buffer_state = IAQ_BUFFER_DEFAULT_VALS; + + if (default_vals) + return -EBUSY; + + data->iaq_buffer_state = IAQ_BUFFER_VALID; + + return 0; +} + +static void sgp_iaq_thread_sleep_until(const struct sgp_data *data, + unsigned long sleep_jiffies) +{ + const long IAQ_POLL = 50000; + + while (!time_after(jiffies, sleep_jiffies)) { + usleep_range(IAQ_POLL, IAQ_POLL + 10000); + if (kthread_should_stop() || data->iaq_init_start_jiffies == 0) + return; + } +} + +static int sgp_iaq_threadfn(void *p) +{ + struct sgp_data *data = (struct sgp_data *)p; + unsigned long next_update_jiffies; + int ret; + + while (!kthread_should_stop()) { + mutex_lock(&data->data_lock); + if (data->iaq_init_start_jiffies == 0) { + ret = sgp_read_cmd(data, data->iaq_init_cmd, NULL, 0, + SGP_CMD_DURATION_US); + if (ret < 0) + goto unlock_sleep_continue; + data->iaq_init_start_jiffies = jiffies; + } + + ret = sgp_measure_iaq(data); + if (ret && ret != -EBUSY) { + dev_warn(&data->client->dev, + "IAQ measurement error [%d]\n", ret); + } +unlock_sleep_continue: + next_update_jiffies = jiffies + data->measure_interval_jiffies; + mutex_unlock(&data->data_lock); + sgp_iaq_thread_sleep_until(data, next_update_jiffies); + } + + return 0; +} + +static int sgp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct sgp_data *data = iio_priv(indio_dev); + struct sgp_crc_word *words; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&data->data_lock); + if (data->iaq_buffer_state != IAQ_BUFFER_VALID) { + mutex_unlock(&data->data_lock); + return -EBUSY; + } + words = data->iaq_buffer.raw_words; + switch (chan->address) { + case SGP30_IAQ_TVOC_IDX: + case SGPC3_IAQ_TVOC_IDX: + *val = 0; + *val2 = be16_to_cpu(words[1].value); + ret = IIO_VAL_INT_PLUS_NANO; + break; + case SGP30_IAQ_CO2EQ_IDX: + *val = 0; + *val2 = be16_to_cpu(words[0].value); + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->data_lock); + break; + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->data_lock); + if (chan->address == SGPC3_SIG_ETOH_IDX) { + if (data->iaq_buffer_state == IAQ_BUFFER_EMPTY) + ret = -EBUSY; + else + ret = 0; + words = data->iaq_buffer.raw_words; + } else { + ret = sgp_read_cmd(data, data->measure_gas_signals_cmd, + &data->buffer, SGP_MEASUREMENT_LEN, + SGP_MEASUREMENT_DURATION_US); + words = data->buffer.raw_words; + } + if (ret) { + mutex_unlock(&data->data_lock); + return ret; + } + + switch (chan->address) { + case SGP30_SIG_ETOH_IDX: + *val = be16_to_cpu(words[1].value); + ret = IIO_VAL_INT; + break; + case SGPC3_SIG_ETOH_IDX: + case SGP30_SIG_H2_IDX: + *val = be16_to_cpu(words[0].value); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->data_lock); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int sgp_check_compat(struct sgp_data *data, + unsigned int product_id) +{ + const struct sgp_version *supported_versions; + u16 ix, num_fs; + u16 product, generation, major, minor; + + /* driver does not match product */ + generation = SGP_VERS_GEN(data); + if (generation != 0) { + dev_err(&data->client->dev, + "incompatible product generation %d != 0", generation); + return -ENODEV; + } + + product = SGP_VERS_PRODUCT(data); + if (product != product_id) { + dev_err(&data->client->dev, + "sensor reports a different product: 0x%04hx\n", + product); + return -ENODEV; + } + + if (SGP_VERS_RESERVED(data)) + dev_warn(&data->client->dev, "reserved bit is set\n"); + + /* engineering samples are not supported: no interface guarantees */ + if (SGP_VERS_ENG_BIT(data)) + return -ENODEV; + + switch (product) { + case SGP30: + supported_versions = supported_versions_sgp30; + num_fs = ARRAY_SIZE(supported_versions_sgp30); + break; + case SGPC3: + supported_versions = supported_versions_sgpc3; + num_fs = ARRAY_SIZE(supported_versions_sgpc3); + break; + default: + return -ENODEV; + } + + major = SGP_VERS_MAJOR(data); + minor = SGP_VERS_MINOR(data); + for (ix = 0; ix < num_fs; ix++) { + if (major == supported_versions[ix].major && + minor >= supported_versions[ix].minor) + return 0; + } + dev_err(&data->client->dev, "unsupported sgp version: %d.%d\n", + major, minor); + + return -ENODEV; +} + +static void sgp_init(struct sgp_data *data) +{ + data->iaq_init_cmd = SGP_CMD_IAQ_INIT; + data->iaq_init_start_jiffies = 0; + data->iaq_buffer_state = IAQ_BUFFER_EMPTY; + switch (SGP_VERS_PRODUCT(data)) { + case SGP30: + data->measure_interval_jiffies = SGP30_MEASURE_INTERVAL_HZ * HZ; + data->measure_iaq_cmd = SGP_CMD_IAQ_MEASURE; + data->measure_gas_signals_cmd = SGP30_CMD_MEASURE_SIGNAL; + data->product_id = SGP30; + data->iaq_defval_skip_jiffies = 15 * HZ; + break; + case SGPC3: + data->measure_interval_jiffies = SGPC3_MEASURE_INTERVAL_HZ * HZ; + data->measure_iaq_cmd = SGPC3_CMD_MEASURE_RAW; + data->measure_gas_signals_cmd = SGPC3_CMD_MEASURE_RAW; + data->product_id = SGPC3; + data->iaq_defval_skip_jiffies = + 43 * data->measure_interval_jiffies; + break; + }; +} + +static const struct iio_info sgp_info = { + .read_raw = sgp_read_raw, +}; + +static const struct of_device_id sgp_dt_ids[] = { + { .compatible = "sensirion,sgp30", .data = (void *)SGP30 }, + { .compatible = "sensirion,sgpc3", .data = (void *)SGPC3 }, + { } +}; + +static int sgp_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct sgp_data *data; + const struct of_device_id *of_id; + unsigned long product_id; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + of_id = of_match_device(sgp_dt_ids, &client->dev); + if (of_id) + product_id = (unsigned long)of_id->data; + else + product_id = id->driver_data; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + crc8_populate_msb(sgp_crc8_table, SGP_CRC8_POLYNOMIAL); + mutex_init(&data->data_lock); + + /* get feature set version and write it to client data */ + ret = sgp_read_cmd(data, SGP_CMD_GET_FEATURE_SET, &data->buffer, 1, + SGP_CMD_DURATION_US); + if (ret < 0) + return ret; + + data->feature_set = be16_to_cpu(data->buffer.raw_words[0].value); + + ret = sgp_check_compat(data, product_id); + if (ret) + return ret; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &sgp_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = sgp_devices[product_id].channels; + indio_dev->num_channels = sgp_devices[product_id].num_channels; + + sgp_init(data); + + ret = devm_iio_device_register(&client->dev, indio_dev); + if (ret) { + dev_err(&client->dev, "failed to register iio device\n"); + return ret; + } + + data->iaq_thread = kthread_run(sgp_iaq_threadfn, data, + "%s-iaq", data->client->name); + + return 0; +} + +static int sgp_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct sgp_data *data = iio_priv(indio_dev); + + if (data->iaq_thread) + kthread_stop(data->iaq_thread); + + return 0; +} + +static const struct i2c_device_id sgp_id[] = { + { "sgp30", SGP30 }, + { "sgpc3", SGPC3 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, sgp_id); +MODULE_DEVICE_TABLE(of, sgp_dt_ids); + +static struct i2c_driver sgp_driver = { + .driver = { + .name = "sgp30", + .of_match_table = of_match_ptr(sgp_dt_ids), + }, + .probe = sgp_probe, + .remove = sgp_remove, + .id_table = sgp_id, +}; +module_i2c_driver(sgp_driver); + +MODULE_AUTHOR("Andreas Brauchli <andreas.brauchli@sensirion.com>"); +MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>"); +MODULE_DESCRIPTION("Sensirion SGP gas sensors"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SPS30 particulate matter sensor driver + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + * + * I2C slave address: 0x69 + */ + +#include <asm/unaligned.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#define SPS30_CRC8_POLYNOMIAL 0x31 +/* max number of bytes needed to store PM measurements or serial string */ +#define SPS30_MAX_READ_SIZE 48 +/* sensor measures reliably up to 3000 ug / m3 */ +#define SPS30_MAX_PM 3000 +/* minimum and maximum self cleaning periods in seconds */ +#define SPS30_AUTO_CLEANING_PERIOD_MIN 0 +#define SPS30_AUTO_CLEANING_PERIOD_MAX 604800 + +/* SPS30 commands */ +#define SPS30_START_MEAS 0x0010 +#define SPS30_STOP_MEAS 0x0104 +#define SPS30_RESET 0xd304 +#define SPS30_READ_DATA_READY_FLAG 0x0202 +#define SPS30_READ_DATA 0x0300 +#define SPS30_READ_SERIAL 0xd033 +#define SPS30_START_FAN_CLEANING 0x5607 +#define SPS30_AUTO_CLEANING_PERIOD 0x8004 +/* not a sensor command per se, used only to distinguish write from read */ +#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005 + +enum { + PM1, + PM2P5, + PM4, + PM10, +}; + +enum { + RESET, + MEASURING, +}; + +struct sps30_state { + struct i2c_client *client; + /* + * Guards against concurrent access to sensor registers. + * Must be held whenever sequence of commands is to be executed. + */ + struct mutex lock; + int state; +}; + +DECLARE_CRC8_TABLE(sps30_crc8_table); + +static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf, + int txsize, u8 *rxbuf, int rxsize) +{ + int ret; + + /* + * Sensor does not support repeated start so instead of + * sending two i2c messages in a row we just send one by one. + */ + ret = i2c_master_send(state->client, txbuf, txsize); + if (ret != txsize) + return ret < 0 ? ret : -EIO; + + if (!rxbuf) + return 0; + + ret = i2c_master_recv(state->client, rxbuf, rxsize); + if (ret != rxsize) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size) +{ + /* + * Internally sensor stores measurements in a following manner: + * + * PM1: upper two bytes, crc8, lower two bytes, crc8 + * PM2P5: upper two bytes, crc8, lower two bytes, crc8 + * PM4: upper two bytes, crc8, lower two bytes, crc8 + * PM10: upper two bytes, crc8, lower two bytes, crc8 + * + * What follows next are number concentration measurements and + * typical particle size measurement which we omit. + */ + u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd }; + int i, ret = 0; + + switch (cmd) { + case SPS30_START_MEAS: + buf[2] = 0x03; + buf[3] = 0x00; + buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); + ret = sps30_write_then_read(state, buf, 5, NULL, 0); + break; + case SPS30_STOP_MEAS: + case SPS30_RESET: + case SPS30_START_FAN_CLEANING: + ret = sps30_write_then_read(state, buf, 2, NULL, 0); + break; + case SPS30_READ_AUTO_CLEANING_PERIOD: + buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8; + buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD; + /* fall through */ + case SPS30_READ_DATA_READY_FLAG: + case SPS30_READ_DATA: + case SPS30_READ_SERIAL: + /* every two data bytes are checksummed */ + size += size / 2; + ret = sps30_write_then_read(state, buf, 2, buf, size); + break; + case SPS30_AUTO_CLEANING_PERIOD: + buf[2] = data[0]; + buf[3] = data[1]; + buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); + buf[5] = data[2]; + buf[6] = data[3]; + buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE); + ret = sps30_write_then_read(state, buf, 8, NULL, 0); + break; + } + + if (ret) + return ret; + + /* validate received data and strip off crc bytes */ + for (i = 0; i < size; i += 3) { + u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE); + + if (crc != buf[i + 2]) { + dev_err(&state->client->dev, + "data integrity check failed\n"); + return -EIO; + } + + *data++ = buf[i]; + *data++ = buf[i + 1]; + } + + return 0; +} + +static s32 sps30_float_to_int_clamped(const u8 *fp) +{ + int val = get_unaligned_be32(fp); + int mantissa = val & GENMASK(22, 0); + /* this is fine since passed float is always non-negative */ + int exp = val >> 23; + int fraction, shift; + + /* special case 0 */ + if (!exp && !mantissa) + return 0; + + exp -= 127; + if (exp < 0) { + /* return values ranging from 1 to 99 */ + return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp); + } + + /* return values ranging from 100 to 300000 */ + shift = 23 - exp; + val = (1 << exp) + (mantissa >> shift); + if (val >= SPS30_MAX_PM) + return SPS30_MAX_PM * 100; + + fraction = mantissa & GENMASK(shift - 1, 0); + + return val * 100 + ((fraction * 100) >> shift); +} + +static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) +{ + int i, ret, tries = 5; + u8 tmp[16]; + + if (state->state == RESET) { + ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0); + if (ret) + return ret; + + state->state = MEASURING; + } + + while (tries--) { + ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2); + if (ret) + return -EIO; + + /* new measurements ready to be read */ + if (tmp[1] == 1) + break; + + msleep_interruptible(300); + } + + if (tries == -1) + return -ETIMEDOUT; + + ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size); + if (ret) + return ret; + + for (i = 0; i < size; i++) + data[i] = sps30_float_to_int_clamped(&tmp[4 * i]); + + return 0; +} + +static irqreturn_t sps30_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct sps30_state *state = iio_priv(indio_dev); + int ret; + s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */ + + mutex_lock(&state->lock); + ret = sps30_do_meas(state, data, 4); + mutex_unlock(&state->lock); + if (ret) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int sps30_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sps30_state *state = iio_priv(indio_dev); + int data[4], ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_MASSCONCENTRATION: + mutex_lock(&state->lock); + /* read up to the number of bytes actually needed */ + switch (chan->channel2) { + case IIO_MOD_PM1: + ret = sps30_do_meas(state, data, 1); + break; + case IIO_MOD_PM2P5: + ret = sps30_do_meas(state, data, 2); + break; + case IIO_MOD_PM4: + ret = sps30_do_meas(state, data, 3); + break; + case IIO_MOD_PM10: + ret = sps30_do_meas(state, data, 4); + break; + } + mutex_unlock(&state->lock); + if (ret) + return ret; + + *val = data[chan->address] / 100; + *val2 = (data[chan->address] % 100) * 10000; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MASSCONCENTRATION: + switch (chan->channel2) { + case IIO_MOD_PM1: + case IIO_MOD_PM2P5: + case IIO_MOD_PM4: + case IIO_MOD_PM10: + *val = 0; + *val2 = 10000; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int sps30_do_cmd_reset(struct sps30_state *state) +{ + int ret; + + ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0); + msleep(300); + /* + * Power-on-reset causes sensor to produce some glitch on i2c bus and + * some controllers end up in error state. Recover simply by placing + * some data on the bus, for example STOP_MEAS command, which + * is NOP in this case. + */ + sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); + state->state = RESET; + + return ret; +} + +static ssize_t start_cleaning_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sps30_state *state = iio_priv(indio_dev); + int val, ret; + + if (kstrtoint(buf, 0, &val) || val != 1) + return -EINVAL; + + mutex_lock(&state->lock); + ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0); + mutex_unlock(&state->lock); + if (ret) + return ret; + + return len; +} + +static ssize_t cleaning_period_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sps30_state *state = iio_priv(indio_dev); + u8 tmp[4]; + int ret; + + mutex_lock(&state->lock); + ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4); + mutex_unlock(&state->lock); + if (ret) + return ret; + + return sprintf(buf, "%d\n", get_unaligned_be32(tmp)); +} + +static ssize_t cleaning_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sps30_state *state = iio_priv(indio_dev); + int val, ret; + u8 tmp[4]; + + if (kstrtoint(buf, 0, &val)) + return -EINVAL; + + if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) || + (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) + return -EINVAL; + + put_unaligned_be32(val, tmp); + + mutex_lock(&state->lock); + ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0); + if (ret) { + mutex_unlock(&state->lock); + return ret; + } + + msleep(20); + + /* + * sensor requires reset in order to return up to date self cleaning + * period + */ + ret = sps30_do_cmd_reset(state); + if (ret) + dev_warn(dev, + "period changed but reads will return the old value\n"); + + mutex_unlock(&state->lock); + + return len; +} + +static ssize_t cleaning_period_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n", + SPS30_AUTO_CLEANING_PERIOD_MIN, 1, + SPS30_AUTO_CLEANING_PERIOD_MAX); +} + +static IIO_DEVICE_ATTR_WO(start_cleaning, 0); +static IIO_DEVICE_ATTR_RW(cleaning_period, 0); +static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0); + +static struct attribute *sps30_attrs[] = { + &iio_dev_attr_start_cleaning.dev_attr.attr, + &iio_dev_attr_cleaning_period.dev_attr.attr, + &iio_dev_attr_cleaning_period_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group sps30_attr_group = { + .attrs = sps30_attrs, +}; + +static const struct iio_info sps30_info = { + .attrs = &sps30_attr_group, + .read_raw = sps30_read_raw, +}; + +#define SPS30_CHAN(_index, _mod) { \ + .type = IIO_MASSCONCENTRATION, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = _mod, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 19, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec sps30_channels[] = { + SPS30_CHAN(0, PM1), + SPS30_CHAN(1, PM2P5), + SPS30_CHAN(2, PM4), + SPS30_CHAN(3, PM10), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static void sps30_stop_meas(void *data) +{ + struct sps30_state *state = data; + + sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); +} + +static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 }; + +static int sps30_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct sps30_state *state; + u8 buf[32]; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + state->client = client; + state->state = RESET; + indio_dev->dev.parent = &client->dev; + indio_dev->info = &sps30_info; + indio_dev->name = client->name; + indio_dev->channels = sps30_channels; + indio_dev->num_channels = ARRAY_SIZE(sps30_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = sps30_scan_masks; + + mutex_init(&state->lock); + crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL); + + ret = sps30_do_cmd_reset(state); + if (ret) { + dev_err(&client->dev, "failed to reset device\n"); + return ret; + } + + ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf)); + if (ret) { + dev_err(&client->dev, "failed to read serial number\n"); + return ret; + } + /* returned serial number is already NUL terminated */ + dev_info(&client->dev, "serial number: %s\n", buf); + + ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + sps30_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id sps30_id[] = { + { "sps30" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sps30_id); + +static const struct of_device_id sps30_of_match[] = { + { .compatible = "sensirion,sps30" }, + { } +}; +MODULE_DEVICE_TABLE(of, sps30_of_match); + +static struct i2c_driver sps30_driver = { + .driver = { + .name = "sps30", + .of_match_table = sps30_of_match, + }, + .id_table = sps30_id, + .probe_new = sps30_probe, +}; +module_i2c_driver(sps30_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); +MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig @@ -148,9 +148,9 @@ config AD5686_SPI depends on SPI select AD5686 help - Say yes here to build support for Analog Devices AD5672R, AD5676, - AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R. - Voltage Output Digital to Analog Converter. + Say yes here to build support for Analog Devices AD5672R, AD5674R, + AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, + AD5686R Voltage Output Digital to Analog Converter. To compile this driver as a module, choose M here: the module will be called ad5686. @@ -375,6 +375,16 @@ config TI_DAC7311 If compiled as a module, it will be called ti-dac7311. +config TI_DAC7612 + tristate "Texas Instruments 12-bit 2-channel DAC driver" + depends on SPI_MASTER && GPIOLIB + help + Driver for the Texas Instruments DAC7612, DAC7612U, DAC7612UB + The driver hand drive the load pin automatically, otherwise + it needs to be toggled manually. + + If compiled as a module, it will be called ti-dac7612. + config VF610_DAC tristate "Vybrid vf610 DAC driver" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile @@ -41,4 +41,5 @@ obj-$(CONFIG_STM32_DAC) += stm32-dac.o obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o +obj-$(CONFIG_TI_DAC7612) += ti-dac7612.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c @@ -1,7 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* - * AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R, - * AD5684, AD5684R, AD5685R, AD5686, AD5686R + * AD5672R, AD5674R, AD5676, AD5676R, AD5679R, + * AD5681R, AD5682R, AD5683, AD5683R, AD5684, + * AD5684R, AD5685R, AD5686, AD5686R * Digital to analog converters driver * * Copyright 2018 Analog Devices Inc. @@ -102,8 +103,10 @@ static int ad5686_spi_remove(struct spi_device *spi) static const struct spi_device_id ad5686_spi_id[] = { {"ad5310r", ID_AD5310R}, {"ad5672r", ID_AD5672R}, + {"ad5674r", ID_AD5674R}, {"ad5676", ID_AD5676}, {"ad5676r", ID_AD5676R}, + {"ad5679r", ID_AD5679R}, {"ad5681r", ID_AD5681R}, {"ad5682r", ID_AD5682R}, {"ad5683", ID_AD5683}, diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * AD5686R, AD5685R, AD5684R Digital to analog converters driver * @@ -71,7 +71,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, int ret; struct ad5686_state *st = iio_priv(indio_dev); unsigned int val, ref_bit_msk; - u8 shift; + u8 shift, address = 0; ret = strtobool(buf, &readin); if (ret) @@ -94,6 +94,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, case AD5686_REGMAP: shift = 0; ref_bit_msk = 0; + /* AD5674R/AD5679R have 16 channels and 2 powerdown registers */ + if (chan->channel > 0x7) + address = 0x8; break; case AD5693_REGMAP: shift = 13; @@ -107,7 +110,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (!st->use_internal_vref) val |= ref_bit_msk; - ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val); + ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, + address, val >> (address * 2)); return ret ? ret : len; } @@ -226,10 +230,32 @@ static struct iio_chan_spec name[] = { \ AD5868_CHANNEL(7, 7, bits, _shift), \ } +#define DECLARE_AD5679_CHANNELS(name, bits, _shift) \ +static struct iio_chan_spec name[] = { \ + AD5868_CHANNEL(0, 0, bits, _shift), \ + AD5868_CHANNEL(1, 1, bits, _shift), \ + AD5868_CHANNEL(2, 2, bits, _shift), \ + AD5868_CHANNEL(3, 3, bits, _shift), \ + AD5868_CHANNEL(4, 4, bits, _shift), \ + AD5868_CHANNEL(5, 5, bits, _shift), \ + AD5868_CHANNEL(6, 6, bits, _shift), \ + AD5868_CHANNEL(7, 7, bits, _shift), \ + AD5868_CHANNEL(8, 8, bits, _shift), \ + AD5868_CHANNEL(9, 9, bits, _shift), \ + AD5868_CHANNEL(10, 10, bits, _shift), \ + AD5868_CHANNEL(11, 11, bits, _shift), \ + AD5868_CHANNEL(12, 12, bits, _shift), \ + AD5868_CHANNEL(13, 13, bits, _shift), \ + AD5868_CHANNEL(14, 14, bits, _shift), \ + AD5868_CHANNEL(15, 15, bits, _shift), \ +} + DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2); DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6); DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); +DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4); DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0); +DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0); DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4); DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); @@ -262,6 +288,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 8, .regmap_type = AD5686_REGMAP, }, + [ID_AD5674R] = { + .channels = ad5674r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5675R] = { .channels = ad5676_channels, .int_vref_mv = 2500, @@ -279,6 +311,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 8, .regmap_type = AD5686_REGMAP, }, + [ID_AD5679R] = { + .channels = ad5679r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5681R] = { .channels = ad5691r_channels, .int_vref_mv = 2500, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * This file is part of AD5686 DAC driver * @@ -54,9 +54,11 @@ enum ad5686_supported_device_ids { ID_AD5311R, ID_AD5671R, ID_AD5672R, + ID_AD5674R, ID_AD5675R, ID_AD5676, ID_AD5676R, + ID_AD5679R, ID_AD5681R, ID_AD5682R, ID_AD5683, diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R, * AD5694, AD5694R, AD5695R, AD5696, AD5696R diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * AD5758 Digital to analog converters driver * diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter + * + * Copyright 2019 Qtechnology A/S + * 2019 Ricardo Ribalda <ricardo@ribalda.com> + * + * Licensed under the GPL-2. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/iio.h> + +#define DAC7612_RESOLUTION 12 +#define DAC7612_ADDRESS 4 +#define DAC7612_START 5 + +struct dac7612 { + struct spi_device *spi; + struct gpio_desc *loaddacs; + uint16_t cache[2]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + uint8_t data[2] ____cacheline_aligned; +}; + +static int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val) +{ + int ret; + + priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS); + priv->data[0] |= val >> 8; + priv->data[1] = val & 0xff; + + priv->cache[channel] = val; + + ret = spi_write(priv->spi, priv->data, sizeof(priv->data)); + if (ret) + return ret; + + gpiod_set_value(priv->loaddacs, 1); + gpiod_set_value(priv->loaddacs, 0); + + return 0; +} + +#define dac7612_CHANNEL(chan, name) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .indexed = 1, \ + .output = 1, \ + .datasheet_name = name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec dac7612_channels[] = { + dac7612_CHANNEL(0, "OUTA"), + dac7612_CHANNEL(1, "OUTB"), +}; + +static int dac7612_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct dac7612 *priv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + priv = iio_priv(iio_dev); + *val = priv->cache[chan->channel]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 1; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int dac7612_write_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct dac7612 *priv = iio_priv(iio_dev); + int ret; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2) + return -EINVAL; + + if (val == priv->cache[chan->channel]) + return 0; + + mutex_lock(&iio_dev->mlock); + ret = dac7612_cmd_single(priv, chan->channel, val); + mutex_unlock(&iio_dev->mlock); + + return ret; +} + +static const struct iio_info dac7612_info = { + .read_raw = dac7612_read_raw, + .write_raw = dac7612_write_raw, +}; + +static int dac7612_probe(struct spi_device *spi) +{ + struct iio_dev *iio_dev; + struct dac7612 *priv; + int i; + int ret; + + iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); + if (!iio_dev) + return -ENOMEM; + + priv = iio_priv(iio_dev); + /* + * LOADDACS pin can be controlled by the driver or externally. + * When controlled by the driver, the DAC value is updated after + * every write. + * When the driver does not control the PIN, the user or an external + * event can change the value of all DACs by pulsing down the LOADDACs + * pin. + */ + priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs", + GPIOD_OUT_LOW); + if (IS_ERR(priv->loaddacs)) + return PTR_ERR(priv->loaddacs); + priv->spi = spi; + spi_set_drvdata(spi, iio_dev); + iio_dev->dev.parent = &spi->dev; + iio_dev->info = &dac7612_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = dac7612_channels; + iio_dev->num_channels = ARRAY_SIZE(priv->cache); + iio_dev->name = spi_get_device_id(spi)->name; + + for (i = 0; i < ARRAY_SIZE(priv->cache); i++) { + ret = dac7612_cmd_single(priv, i, 0); + if (ret) + return ret; + } + + return devm_iio_device_register(&spi->dev, iio_dev); +} + +static const struct spi_device_id dac7612_id[] = { + {"ti-dac7612"}, + {} +}; +MODULE_DEVICE_TABLE(spi, dac7612_id); + +static const struct of_device_id dac7612_of_match[] = { + { .compatible = "ti,dac7612" }, + { .compatible = "ti,dac7612u" }, + { .compatible = "ti,dac7612ub" }, + { }, +}; +MODULE_DEVICE_TABLE(of, dac7612_of_match); + +static struct spi_driver dac7612_driver = { + .driver = { + .name = "ti-dac7612", + .of_match_table = dac7612_of_match, + }, + .probe = dac7612_probe, + .id_table = dac7612_id, +}; +module_spi_driver(dac7612_driver); + +MODULE_AUTHOR("Ricardo Ribalda <ricardo@ribalda.com>"); +MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c @@ -943,11 +943,14 @@ static int ad9523_setup(struct iio_dev *indio_dev) } } - for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) - ad9523_write(indio_dev, + for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) { + ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(i), AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) | AD9523_CLK_DIST_PWR_DOWN_EN); + if (ret < 0) + return ret; + } ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0); if (ret < 0) diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h @@ -2,9 +2,20 @@ #ifndef BMI160_H_ #define BMI160_H_ +#include <linux/iio/iio.h> + +struct bmi160_data { + struct regmap *regmap; + struct iio_trigger *trig; +}; + extern const struct regmap_config bmi160_regmap_config; int bmi160_core_probe(struct device *dev, struct regmap *regmap, const char *name, bool use_spi); +int bmi160_enable_irq(struct regmap *regmap, bool enable); + +int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type); + #endif /* BMI160_H_ */ diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c @@ -1,26 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BMI160 - Bosch IMU (accel, gyro plus external magnetometer) * * Copyright (c) 2016, Intel Corporation. - * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. + * Copyright (c) 2019, Martin Kelly. * * IIO core driver for BMI160, with support for I2C/SPI busses * - * TODO: magnetometer, interrupts, hardware FIFO + * TODO: magnetometer, hardware FIFO */ #include <linux/module.h> #include <linux/regmap.h> #include <linux/acpi.h> #include <linux/delay.h> +#include <linux/irq.h> +#include <linux/of_irq.h> #include <linux/iio/iio.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/buffer.h> #include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> #include "bmi160.h" @@ -64,8 +65,32 @@ #define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17 #define BMI160_CMD_SOFTRESET 0xB6 +#define BMI160_REG_INT_EN 0x51 +#define BMI160_DRDY_INT_EN BIT(4) + +#define BMI160_REG_INT_OUT_CTRL 0x53 +#define BMI160_INT_OUT_CTRL_MASK 0x0f +#define BMI160_INT1_OUT_CTRL_SHIFT 0 +#define BMI160_INT2_OUT_CTRL_SHIFT 4 +#define BMI160_EDGE_TRIGGERED BIT(0) +#define BMI160_ACTIVE_HIGH BIT(1) +#define BMI160_OPEN_DRAIN BIT(2) +#define BMI160_OUTPUT_EN BIT(3) + +#define BMI160_REG_INT_LATCH 0x54 +#define BMI160_INT1_LATCH_MASK BIT(4) +#define BMI160_INT2_LATCH_MASK BIT(5) + +/* INT1 and INT2 are in the opposite order as in INT_OUT_CTRL! */ +#define BMI160_REG_INT_MAP 0x56 +#define BMI160_INT1_MAP_DRDY_EN 0x80 +#define BMI160_INT2_MAP_DRDY_EN 0x08 + #define BMI160_REG_DUMMY 0x7F +#define BMI160_NORMAL_WRITE_USLEEP 2 +#define BMI160_SUSPENDED_WRITE_USLEEP 450 + #define BMI160_ACCEL_PMU_MIN_USLEEP 3800 #define BMI160_GYRO_PMU_MIN_USLEEP 80000 #define BMI160_SOFTRESET_USLEEP 1000 @@ -108,8 +133,9 @@ enum bmi160_sensor_type { BMI160_NUM_SENSORS /* must be last */ }; -struct bmi160_data { - struct regmap *regmap; +enum bmi160_int_pin { + BMI160_PIN_INT1, + BMI160_PIN_INT2 }; const struct regmap_config bmi160_regmap_config = { @@ -273,7 +299,7 @@ int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t, cmd = bmi160_regs[t].pmu_cmd_suspend; ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd); - if (ret < 0) + if (ret) return ret; usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000); @@ -305,7 +331,7 @@ int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t, int i, ret, val; ret = regmap_read(data->regmap, bmi160_regs[t].range, &val); - if (ret < 0) + if (ret) return ret; for (i = 0; i < bmi160_scale_table[t].num; i++) @@ -328,7 +354,7 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type, reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample); ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample)); - if (ret < 0) + if (ret) return ret; *val = sign_extend32(le16_to_cpu(sample), 15); @@ -362,7 +388,7 @@ static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t, int i, val, ret; ret = regmap_read(data->regmap, bmi160_regs[t].config, &val); - if (ret < 0) + if (ret) return ret; val &= bmi160_regs[t].config_odr_mask; @@ -394,13 +420,12 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p) indio_dev->masklength) { ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample), &sample, sizeof(sample)); - if (ret < 0) + if (ret) goto done; buf[j++] = sample; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -416,18 +441,18 @@ static int bmi160_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: ret = bmi160_get_data(data, chan->type, chan->channel2, val); - if (ret < 0) + if (ret) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; ret = bmi160_get_scale(data, bmi160_to_sensor(chan->type), val2); - return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO; + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type), val, val2); - return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO; + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -498,6 +523,186 @@ static const char *bmi160_match_acpi_device(struct device *dev) return dev_name(dev); } +static int bmi160_write_conf_reg(struct regmap *regmap, unsigned int reg, + unsigned int mask, unsigned int bits, + unsigned int write_usleep) +{ + int ret; + unsigned int val; + + ret = regmap_read(regmap, reg, &val); + if (ret) + return ret; + + val = (val & ~mask) | bits; + + ret = regmap_write(regmap, reg, val); + if (ret) + return ret; + + /* + * We need to wait after writing before we can write again. See the + * datasheet, page 93. + */ + usleep_range(write_usleep, write_usleep + 1000); + + return 0; +} + +static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin, + bool open_drain, u8 irq_mask, + unsigned long write_usleep) +{ + int ret; + struct device *dev = regmap_get_device(regmap); + u8 int_out_ctrl_shift; + u8 int_latch_mask; + u8 int_map_mask; + u8 int_out_ctrl_mask; + u8 int_out_ctrl_bits; + const char *pin_name; + + switch (pin) { + case BMI160_PIN_INT1: + int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT; + int_latch_mask = BMI160_INT1_LATCH_MASK; + int_map_mask = BMI160_INT1_MAP_DRDY_EN; + break; + case BMI160_PIN_INT2: + int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT; + int_latch_mask = BMI160_INT2_LATCH_MASK; + int_map_mask = BMI160_INT2_MAP_DRDY_EN; + break; + } + int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift; + + /* + * Enable the requested pin with the right settings: + * - Push-pull/open-drain + * - Active low/high + * - Edge/level triggered + */ + int_out_ctrl_bits = BMI160_OUTPUT_EN; + if (open_drain) + /* Default is push-pull. */ + int_out_ctrl_bits |= BMI160_OPEN_DRAIN; + int_out_ctrl_bits |= irq_mask; + int_out_ctrl_bits <<= int_out_ctrl_shift; + + ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_OUT_CTRL, + int_out_ctrl_mask, int_out_ctrl_bits, + write_usleep); + if (ret) + return ret; + + /* Set the pin to input mode with no latching. */ + ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_LATCH, + int_latch_mask, int_latch_mask, + write_usleep); + if (ret) + return ret; + + /* Map interrupts to the requested pin. */ + ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP, + int_map_mask, int_map_mask, + write_usleep); + if (ret) { + switch (pin) { + case BMI160_PIN_INT1: + pin_name = "INT1"; + break; + case BMI160_PIN_INT2: + pin_name = "INT2"; + break; + } + dev_err(dev, "Failed to configure %s IRQ pin", pin_name); + } + + return ret; +} + +int bmi160_enable_irq(struct regmap *regmap, bool enable) +{ + unsigned int enable_bit = 0; + + if (enable) + enable_bit = BMI160_DRDY_INT_EN; + + return bmi160_write_conf_reg(regmap, BMI160_REG_INT_EN, + BMI160_DRDY_INT_EN, enable_bit, + BMI160_NORMAL_WRITE_USLEEP); +} +EXPORT_SYMBOL(bmi160_enable_irq); + +static int bmi160_get_irq(struct device_node *of_node, enum bmi160_int_pin *pin) +{ + int irq; + + /* Use INT1 if possible, otherwise fall back to INT2. */ + irq = of_irq_get_byname(of_node, "INT1"); + if (irq > 0) { + *pin = BMI160_PIN_INT1; + return irq; + } + + irq = of_irq_get_byname(of_node, "INT2"); + if (irq > 0) + *pin = BMI160_PIN_INT2; + + return irq; +} + +static int bmi160_config_device_irq(struct iio_dev *indio_dev, int irq_type, + enum bmi160_int_pin pin) +{ + bool open_drain; + u8 irq_mask; + struct bmi160_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + + /* Level-triggered, active-low is the default if we set all zeroes. */ + if (irq_type == IRQF_TRIGGER_RISING) + irq_mask = BMI160_ACTIVE_HIGH | BMI160_EDGE_TRIGGERED; + else if (irq_type == IRQF_TRIGGER_FALLING) + irq_mask = BMI160_EDGE_TRIGGERED; + else if (irq_type == IRQF_TRIGGER_HIGH) + irq_mask = BMI160_ACTIVE_HIGH; + else if (irq_type == IRQF_TRIGGER_LOW) + irq_mask = 0; + else { + dev_err(&indio_dev->dev, + "Invalid interrupt type 0x%x specified\n", irq_type); + return -EINVAL; + } + + open_drain = of_property_read_bool(dev->of_node, "drive-open-drain"); + + return bmi160_config_pin(data->regmap, pin, open_drain, irq_mask, + BMI160_NORMAL_WRITE_USLEEP); +} + +static int bmi160_setup_irq(struct iio_dev *indio_dev, int irq, + enum bmi160_int_pin pin) +{ + struct irq_data *desc; + u32 irq_type; + int ret; + + desc = irq_get_irq_data(irq); + if (!desc) { + dev_err(&indio_dev->dev, "Could not find IRQ %d\n", irq); + return -EINVAL; + } + + irq_type = irqd_get_trigger_type(desc); + + ret = bmi160_config_device_irq(indio_dev, irq_type, pin); + if (ret) + return ret; + + return bmi160_probe_trigger(indio_dev, irq, irq_type); +} + static int bmi160_chip_init(struct bmi160_data *data, bool use_spi) { int ret; @@ -505,7 +710,7 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi) struct device *dev = regmap_get_device(data->regmap); ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET); - if (ret < 0) + if (ret) return ret; usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1); @@ -516,12 +721,12 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi) */ if (use_spi) { ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val); - if (ret < 0) + if (ret) return ret; } ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val); - if (ret < 0) { + if (ret) { dev_err(dev, "Error reading chip id\n"); return ret; } @@ -532,16 +737,59 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi) } ret = bmi160_set_mode(data, BMI160_ACCEL, true); - if (ret < 0) + if (ret) return ret; ret = bmi160_set_mode(data, BMI160_GYRO, true); - if (ret < 0) + if (ret) return ret; return 0; } +static int bmi160_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool enable) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct bmi160_data *data = iio_priv(indio_dev); + + return bmi160_enable_irq(data->regmap, enable); +} + +static const struct iio_trigger_ops bmi160_trigger_ops = { + .set_trigger_state = &bmi160_data_rdy_trigger_set_state, +}; + +int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type) +{ + struct bmi160_data *data = iio_priv(indio_dev); + int ret; + + data->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + + if (data->trig == NULL) + return -ENOMEM; + + ret = devm_request_irq(&indio_dev->dev, irq, + &iio_trigger_generic_data_rdy_poll, + irq_type, "bmi160", data->trig); + if (ret) + return ret; + + data->trig->dev.parent = regmap_get_device(data->regmap); + data->trig->ops = &bmi160_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + + ret = devm_iio_trigger_register(&indio_dev->dev, data->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(data->trig); + + return 0; +} + static void bmi160_chip_uninit(void *data) { struct bmi160_data *bmi_data = data; @@ -555,6 +803,8 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap, { struct iio_dev *indio_dev; struct bmi160_data *data; + int irq; + enum bmi160_int_pin int_pin; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -566,11 +816,11 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap, data->regmap = regmap; ret = bmi160_chip_init(data, use_spi); - if (ret < 0) + if (ret) return ret; ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data); - if (ret < 0) + if (ret) return ret; if (!name && ACPI_HANDLE(dev)) @@ -583,16 +833,23 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &bmi160_info; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, bmi160_trigger_handler, NULL); - if (ret < 0) + if (ret) return ret; - ret = devm_iio_device_register(dev, indio_dev); - if (ret < 0) - return ret; + irq = bmi160_get_irq(dev->of_node, &int_pin); + if (irq > 0) { + ret = bmi160_setup_irq(indio_dev, irq, int_pin); + if (ret) + dev_err(&indio_dev->dev, "Failed to setup IRQ %d\n", + irq); + } else { + dev_info(&indio_dev->dev, "Not setting up IRQ trigger\n"); + } - return 0; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_GPL(bmi160_core_probe); diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BMI160 - Bosch IMU, I2C bits * * Copyright (c) 2016, Intel Corporation. * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. - * * 7-bit I2C slave address is: * - 0x68 if SDO is pulled to GND * - 0x69 if SDO is pulled to VDDIO diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -1,11 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BMI160 - Bosch IMU, SPI bits * * Copyright (c) 2016, Intel Corporation. * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. */ #include <linux/acpi.h> #include <linux/module.h> diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -13,8 +13,8 @@ config INV_MPU6050_I2C select INV_MPU6050_IIO select REGMAP_I2C help - This driver supports the Invensense MPU6050/6500/9150 and ICM20608 - motion tracking devices over I2C. + This driver supports the Invensense MPU6050/6500/9150 and + ICM20608/20602 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -24,7 +24,7 @@ config INV_MPU6050_SPI select INV_MPU6050_IIO select REGMAP_SPI help - This driver supports the Invensense MPU6050/6500/9150 and ICM20608 - motion tracking devices over SPI. + This driver supports the Invensense MPU6050/6500/9150 and + ICM20608/20602 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -38,6 +38,29 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724}; */ static const int accel_scale[] = {598, 1196, 2392, 4785}; +static const struct inv_mpu6050_reg_map reg_set_icm20602 = { + .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV, + .lpf = INV_MPU6050_REG_CONFIG, + .accel_lpf = INV_MPU6500_REG_ACCEL_CONFIG_2, + .user_ctrl = INV_MPU6050_REG_USER_CTRL, + .fifo_en = INV_MPU6050_REG_FIFO_EN, + .gyro_config = INV_MPU6050_REG_GYRO_CONFIG, + .accl_config = INV_MPU6050_REG_ACCEL_CONFIG, + .fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H, + .fifo_r_w = INV_MPU6050_REG_FIFO_R_W, + .raw_gyro = INV_MPU6050_REG_RAW_GYRO, + .raw_accl = INV_MPU6050_REG_RAW_ACCEL, + .temperature = INV_MPU6050_REG_TEMPERATURE, + .int_enable = INV_MPU6050_REG_INT_ENABLE, + .int_status = INV_MPU6050_REG_INT_STATUS, + .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, + .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, + .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, + .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET, + .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, + .i2c_if = INV_ICM20602_REG_I2C_IF, +}; + static const struct inv_mpu6050_reg_map reg_set_6500 = { .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV, .lpf = INV_MPU6050_REG_CONFIG, @@ -58,6 +81,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = { .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET, .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, + .i2c_if = 0, }; static const struct inv_mpu6050_reg_map reg_set_6050 = { @@ -78,6 +102,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = { .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, .accl_offset = INV_MPU6050_REG_ACCEL_OFFSET, .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, + .i2c_if = 0, }; static const struct inv_mpu6050_chip_config chip_config_6050 = { @@ -140,6 +165,12 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = &reg_set_6500, .config = &chip_config_6050, }, + { + .whoami = INV_ICM20602_WHOAMI_VALUE, + .name = "ICM20602", + .reg = &reg_set_icm20602, + .config = &chip_config_6050, + }, }; int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -127,6 +127,7 @@ static int inv_mpu_probe(struct i2c_client *client, st = iio_priv(dev_get_drvdata(&client->dev)); switch (st->chip_type) { case INV_ICM20608: + case INV_ICM20602: /* no i2c auxiliary bus on the chip */ break; default: @@ -179,6 +180,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, + {"icm20602", INV_ICM20602}, {} }; @@ -213,6 +215,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 }, + { + .compatible = "invensense,icm20602", + .data = (void *)INV_ICM20602 + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -44,6 +44,7 @@ * @int_pin_cfg; Controls interrupt pin configuration. * @accl_offset: Controls the accelerometer calibration offset. * @gyro_offset: Controls the gyroscope calibration offset. + * @i2c_if: Controls the i2c interface */ struct inv_mpu6050_reg_map { u8 sample_rate_div; @@ -65,6 +66,7 @@ struct inv_mpu6050_reg_map { u8 int_pin_cfg; u8 accl_offset; u8 gyro_offset; + u8 i2c_if; }; /*device enum */ @@ -77,6 +79,7 @@ enum inv_devices { INV_MPU9250, INV_MPU9255, INV_ICM20608, + INV_ICM20602, INV_NUM_PARTS }; @@ -195,6 +198,10 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 #define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 +/* ICM20602 register */ +#define INV_ICM20602_REG_I2C_IF 0x70 +#define INV_ICM20602_BIT_I2C_IF_DIS 0x40 + #define INV_MPU6050_REG_FIFO_COUNT_H 0x72 #define INV_MPU6050_REG_FIFO_R_W 0x74 @@ -261,6 +268,7 @@ struct inv_mpu6050_state { #define INV_MPU9255_WHOAMI_VALUE 0x73 #define INV_MPU6515_WHOAMI_VALUE 0x74 #define INV_ICM20608_WHOAMI_VALUE 0xAF +#define INV_ICM20602_WHOAMI_VALUE 0x12 /* scan element definition */ enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -31,9 +31,14 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) if (ret) return ret; - st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS; - ret = regmap_write(st->map, st->reg->user_ctrl, - st->chip_config.user_ctrl); + if (st->reg->i2c_if) { + ret = regmap_write(st->map, st->reg->i2c_if, + INV_ICM20602_BIT_I2C_IF_DIS); + } else { + st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS; + ret = regmap_write(st->map, st->reg->user_ctrl, + st->chip_config.user_ctrl); + } if (ret) { inv_mpu6050_set_power_itg(st, false); return ret; @@ -81,6 +86,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, + {"icm20602", INV_ICM20602}, {} }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -105,12 +105,10 @@ static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr, u8 *data, int len) { - const struct st_lsm6dsx_shub_settings *hub_settings; int err; mutex_lock(&hw->page_lock); - hub_settings = &hw->settings->shub_settings; err = st_lsm6dsx_set_page(hw, true); if (err < 0) goto out; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c @@ -87,6 +87,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_GRAVITY] = "gravity", [IIO_POSITIONRELATIVE] = "positionrelative", [IIO_PHASE] = "phase", + [IIO_MASSCONCENTRATION] = "massconcentration", }; static const char * const iio_modifier_names[] = { @@ -127,6 +128,10 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_Q] = "q", [IIO_MOD_CO2] = "co2", [IIO_MOD_VOC] = "voc", + [IIO_MOD_PM1] = "pm1", + [IIO_MOD_PM2P5] = "pm2p5", + [IIO_MOD_PM4] = "pm4", + [IIO_MOD_PM10] = "pm10", }; /* relies on pairs of these shared then separate */ diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig @@ -299,6 +299,16 @@ config MAX44000 To compile this driver as a module, choose M here: the module will be called max44000. +config MAX44009 + tristate "MAX44009 Ambient Light Sensor" + depends on I2C + help + Say Y here if you want to build support for Maxim Integrated's + MAX44009 ambient light sensor device. + + To compile this driver as a module, choose M here: + the module will be called max44009. + config OPT3001 tristate "Texas Instruments OPT3001 Light Sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_LTR501) += ltr501.o obj-$(CONFIG_LV0104CS) += lv0104cs.o obj-$(CONFIG_MAX44000) += max44000.o +obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_RPR0521) += rpr0521.o diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c @@ -23,6 +23,7 @@ #include <linux/mutex.h> #include <linux/delay.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -95,6 +96,7 @@ struct isl29018_chip { struct isl29018_scale scale; int prox_scheme; bool suspended; + struct regulator *vcc_reg; }; static int isl29018_set_integration_time(struct isl29018_chip *chip, @@ -708,6 +710,16 @@ static const char *isl29018_match_acpi_device(struct device *dev, int *data) return dev_name(dev); } +static void isl29018_disable_regulator_action(void *_data) +{ + struct isl29018_chip *chip = _data; + int err; + + err = regulator_disable(chip->vcc_reg); + if (err) + pr_err("failed to disable isl29018's VCC regulator!\n"); +} + static int isl29018_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -742,6 +754,27 @@ static int isl29018_probe(struct i2c_client *client, chip->scale = isl29018_scales[chip->int_time][0]; chip->suspended = false; + chip->vcc_reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(chip->vcc_reg)) { + err = PTR_ERR(chip->vcc_reg); + if (err != -EPROBE_DEFER) + dev_err(&client->dev, "failed to get VCC regulator!\n"); + return err; + } + + err = regulator_enable(chip->vcc_reg); + if (err) { + dev_err(&client->dev, "failed to enable VCC regulator!\n"); + return err; + } + + err = devm_add_action_or_reset(&client->dev, isl29018_disable_regulator_action, + chip); + if (err) { + dev_err(&client->dev, "failed to setup regulator cleanup action!\n"); + return err; + } + chip->regmap = devm_regmap_init_i2c(client, isl29018_chip_info_tbl[dev_id].regmap_cfg); if (IS_ERR(chip->regmap)) { @@ -768,6 +801,7 @@ static int isl29018_probe(struct i2c_client *client, static int isl29018_suspend(struct device *dev) { struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev)); + int ret; mutex_lock(&chip->lock); @@ -777,10 +811,13 @@ static int isl29018_suspend(struct device *dev) * So we do not have much to do here. */ chip->suspended = true; + ret = regulator_disable(chip->vcc_reg); + if (ret) + dev_err(dev, "failed to disable VCC regulator\n"); mutex_unlock(&chip->lock); - return 0; + return ret; } static int isl29018_resume(struct device *dev) @@ -790,6 +827,13 @@ static int isl29018_resume(struct device *dev) mutex_lock(&chip->lock); + err = regulator_enable(chip->vcc_reg); + if (err) { + dev_err(dev, "failed to enable VCC regulator\n"); + mutex_unlock(&chip->lock); + return err; + } + err = isl29018_chip_init(chip); if (!err) chip->suspended = false; diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * max44009.c - Support for MAX44009 Ambient Light Sensor + * + * Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com> + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf + * + * TODO: Support continuous mode and configuring from manual mode to + * automatic mode. + * + * Default I2C address: 0x4a + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/util_macros.h> + +#define MAX44009_DRV_NAME "max44009" + +/* Registers in datasheet order */ +#define MAX44009_REG_INT_STATUS 0x0 +#define MAX44009_REG_INT_EN 0x1 +#define MAX44009_REG_CFG 0x2 +#define MAX44009_REG_LUX_HI 0x3 +#define MAX44009_REG_LUX_LO 0x4 +#define MAX44009_REG_UPPER_THR 0x5 +#define MAX44009_REG_LOWER_THR 0x6 +#define MAX44009_REG_THR_TIMER 0x7 + +#define MAX44009_CFG_TIM_MASK GENMASK(2, 0) +#define MAX44009_CFG_MAN_MODE_MASK BIT(6) + +/* The maximum rising threshold for the max44009 */ +#define MAX44009_MAXIMUM_THRESHOLD 7520256 + +#define MAX44009_THRESH_EXP_MASK (0xf << 4) +#define MAX44009_THRESH_EXP_RSHIFT 4 +#define MAX44009_THRESH_MANT_LSHIFT 4 +#define MAX44009_THRESH_MANT_MASK 0xf + +#define MAX44009_UPPER_THR_MINIMUM 15 + +/* The max44009 always scales raw readings by 0.045 and is non-configurable */ +#define MAX44009_SCALE_NUMERATOR 45 +#define MAX44009_SCALE_DENOMINATOR 1000 + +/* The fixed-point fractional multiplier for de-scaling threshold values */ +#define MAX44009_FRACT_MULT 1000000 + +static const u32 max44009_int_time_ns_array[] = { + 800000000, + 400000000, + 200000000, + 100000000, + 50000000, /* Manual mode only */ + 25000000, /* Manual mode only */ + 12500000, /* Manual mode only */ + 6250000, /* Manual mode only */ +}; + +static const char max44009_int_time_str[] = + "0.8 " + "0.4 " + "0.2 " + "0.1 " + "0.05 " + "0.025 " + "0.0125 " + "0.00625"; + +struct max44009_data { + struct i2c_client *client; + struct mutex lock; +}; + +static const struct iio_event_spec max44009_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec max44009_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_INT_TIME), + .event_spec = max44009_event_spec, + .num_event_specs = ARRAY_SIZE(max44009_event_spec), + }, +}; + +static int max44009_read_int_time(struct max44009_data *data) +{ + + int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG); + + if (ret < 0) + return ret; + + return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK]; +} + +static int max44009_write_int_time(struct max44009_data *data, + int val, int val2) +{ + struct i2c_client *client = data->client; + int ret, int_time, config; + s64 ns; + + ns = val * NSEC_PER_SEC + val2; + int_time = find_closest_descending( + ns, + max44009_int_time_ns_array, + ARRAY_SIZE(max44009_int_time_ns_array)); + + ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG); + if (ret < 0) + return ret; + + config = ret; + config &= int_time; + + /* + * To set the integration time, the device must also be in manual + * mode. + */ + config |= MAX44009_CFG_MAN_MODE_MASK; + + return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config); +} + +static int max44009_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct max44009_data *data = iio_priv(indio_dev); + int ret; + + if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) { + mutex_lock(&data->lock); + ret = max44009_write_int_time(data, val, val2); + mutex_unlock(&data->lock); + return ret; + } + return -EINVAL; +} + +static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static int max44009_lux_raw(u8 hi, u8 lo) +{ + int mantissa; + int exponent; + + /* + * The mantissa consists of the low nibble of the Lux High Byte + * and the low nibble of the Lux Low Byte. + */ + mantissa = ((hi & 0xf) << 4) | (lo & 0xf); + + /* The exponent byte is just the upper nibble of the Lux High Byte */ + exponent = (hi >> 4) & 0xf; + + /* + * The exponent value is base 2 to the power of the raw exponent byte. + */ + exponent = 1 << exponent; + + return exponent * mantissa; +} + +#define MAX44009_READ_LUX_XFER_LEN (4) + +static int max44009_read_lux_raw(struct max44009_data *data) +{ + int ret; + u8 hireg = MAX44009_REG_LUX_HI; + u8 loreg = MAX44009_REG_LUX_LO; + u8 lo = 0; + u8 hi = 0; + + struct i2c_msg msgs[] = { + { + .addr = data->client->addr, + .flags = 0, + .len = sizeof(hireg), + .buf = &hireg, + }, + { + .addr = data->client->addr, + .flags = I2C_M_RD, + .len = sizeof(hi), + .buf = &hi, + }, + { + .addr = data->client->addr, + .flags = 0, + .len = sizeof(loreg), + .buf = &loreg, + }, + { + .addr = data->client->addr, + .flags = I2C_M_RD, + .len = sizeof(lo), + .buf = &lo, + } + }; + + /* + * Use i2c_transfer instead of smbus read because i2c_transfer + * does NOT use a stop bit between address write and data read. + * Using a stop bit causes disjoint upper/lower byte reads and + * reduces accuracy. + */ + ret = i2c_transfer(data->client->adapter, + msgs, MAX44009_READ_LUX_XFER_LEN); + + if (ret != MAX44009_READ_LUX_XFER_LEN) + return -EIO; + + return max44009_lux_raw(hi, lo); +} + +static int max44009_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct max44009_data *data = iio_priv(indio_dev); + int lux_raw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_LIGHT: + ret = max44009_read_lux_raw(data); + if (ret < 0) + return ret; + lux_raw = ret; + + *val = lux_raw * MAX44009_SCALE_NUMERATOR; + *val2 = MAX44009_SCALE_DENOMINATOR; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_INT_TIME: + switch (chan->type) { + case IIO_LIGHT: + ret = max44009_read_int_time(data); + if (ret < 0) + return ret; + + *val2 = ret; + *val = 0; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(illuminance_integration_time_available, + max44009_int_time_str); + +static struct attribute *max44009_attributes[] = { + &iio_const_attr_illuminance_integration_time_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max44009_attribute_group = { + .attrs = max44009_attributes, +}; + +static int max44009_threshold_byte_from_fraction(int integral, int fractional) +{ + int mantissa, exp; + + if ((integral <= 0 && fractional <= 0) || + integral > MAX44009_MAXIMUM_THRESHOLD || + (integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0)) + return -EINVAL; + + /* Reverse scaling of fixed-point integral */ + mantissa = integral * MAX44009_SCALE_DENOMINATOR; + mantissa /= MAX44009_SCALE_NUMERATOR; + + /* Reverse scaling of fixed-point fractional */ + mantissa += fractional / MAX44009_FRACT_MULT * + (MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR); + + for (exp = 0; mantissa > 0xff; exp++) + mantissa >>= 1; + + mantissa >>= 4; + mantissa &= 0xf; + exp <<= 4; + + return exp | mantissa; +} + +static int max44009_get_thr_reg(enum iio_event_direction dir) +{ + switch (dir) { + case IIO_EV_DIR_RISING: + return MAX44009_REG_UPPER_THR; + case IIO_EV_DIR_FALLING: + return MAX44009_REG_LOWER_THR; + default: + return -EINVAL; + } +} + +static int max44009_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct max44009_data *data = iio_priv(indio_dev); + int reg, threshold; + + if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT) + return -EINVAL; + + threshold = max44009_threshold_byte_from_fraction(val, val2); + if (threshold < 0) + return threshold; + + reg = max44009_get_thr_reg(dir); + if (reg < 0) + return reg; + + return i2c_smbus_write_byte_data(data->client, reg, threshold); +} + +static int max44009_read_threshold(struct iio_dev *indio_dev, + enum iio_event_direction dir) +{ + struct max44009_data *data = iio_priv(indio_dev); + int byte, reg; + int mantissa, exponent; + + reg = max44009_get_thr_reg(dir); + if (reg < 0) + return reg; + + byte = i2c_smbus_read_byte_data(data->client, reg); + if (byte < 0) + return byte; + + mantissa = byte & MAX44009_THRESH_MANT_MASK; + mantissa <<= MAX44009_THRESH_MANT_LSHIFT; + + /* + * To get the upper threshold, always adds the minimum upper threshold + * value to the shifted byte value (see datasheet). + */ + if (dir == IIO_EV_DIR_RISING) + mantissa += MAX44009_UPPER_THR_MINIMUM; + + /* + * Exponent is base 2 to the power of the threshold exponent byte + * value + */ + exponent = byte & MAX44009_THRESH_EXP_MASK; + exponent >>= MAX44009_THRESH_EXP_RSHIFT; + + return (1 << exponent) * mantissa; +} + +static int max44009_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + int threshold; + + if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + ret = max44009_read_threshold(indio_dev, dir); + if (ret < 0) + return ret; + threshold = ret; + + *val = threshold * MAX44009_SCALE_NUMERATOR; + *val2 = MAX44009_SCALE_DENOMINATOR; + + return IIO_VAL_FRACTIONAL; +} + +static int max44009_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct max44009_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(data->client, + MAX44009_REG_INT_EN, state); + if (ret < 0) + return ret; + + /* + * Set device to trigger interrupt immediately upon exceeding + * the threshold limit. + */ + return i2c_smbus_write_byte_data(data->client, + MAX44009_REG_THR_TIMER, 0); +} + +static int max44009_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct max44009_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN); +} + +static const struct iio_info max44009_info = { + .read_raw = max44009_read_raw, + .write_raw = max44009_write_raw, + .write_raw_get_fmt = max44009_write_raw_get_fmt, + .read_event_value = max44009_read_event_value, + .read_event_config = max44009_read_event_config, + .write_event_value = max44009_write_event_value, + .write_event_config = max44009_write_event_config, + .attrs = &max44009_attribute_group, +}; + +static irqreturn_t max44009_threaded_irq_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct max44009_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS); + if (ret) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int max44009_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max44009_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + indio_dev->dev.parent = &client->dev; + indio_dev->info = &max44009_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = MAX44009_DRV_NAME; + indio_dev->channels = max44009_channels; + indio_dev->num_channels = ARRAY_SIZE(max44009_channels); + mutex_init(&data->lock); + + /* Clear any stale interrupt bit */ + ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG); + if (ret < 0) + return ret; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + max44009_threaded_irq_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT | IRQF_SHARED, + "max44009_event", + indio_dev); + if (ret < 0) + return ret; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id max44009_id[] = { + { "max44009", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max44009_id); + +static struct i2c_driver max44009_driver = { + .driver = { + .name = MAX44009_DRV_NAME, + }, + .probe = max44009_probe, + .id_table = max44009_id, +}; +module_i2c_driver(max44009_driver); + +static const struct of_device_id max44009_of_match[] = { + { .compatible = "maxim,max44009" }, + { } +}; +MODULE_DEVICE_TABLE(of, max44009_of_match); + +MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX44009 ambient light sensor driver"); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c @@ -20,6 +20,7 @@ #include <linux/iio/buffer.h> #include <linux/iio/triggered_buffer.h> #include <linux/delay.h> +#include <linux/regulator/consumer.h> #define MAG3110_STATUS 0x00 #define MAG3110_OUT_X 0x01 /* MSB first */ @@ -56,6 +57,8 @@ struct mag3110_data { struct mutex lock; u8 ctrl_reg1; int sleep_val; + struct regulator *vdd_reg; + struct regulator *vddio_reg; }; static int mag3110_request(struct mag3110_data *data) @@ -469,17 +472,50 @@ static int mag3110_probe(struct i2c_client *client, struct iio_dev *indio_dev; int ret; - ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I); - if (ret < 0) - return ret; - if (ret != MAG3110_DEVICE_ID) - return -ENODEV; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); + + data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_reg)) { + if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(&client->dev, "failed to get VDD regulator!\n"); + return PTR_ERR(data->vdd_reg); + } + + data->vddio_reg = devm_regulator_get(&client->dev, "vddio"); + if (IS_ERR(data->vddio_reg)) { + if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(&client->dev, "failed to get VDDIO regulator!\n"); + return PTR_ERR(data->vddio_reg); + } + + ret = regulator_enable(data->vdd_reg); + if (ret) { + dev_err(&client->dev, "failed to enable VDD regulator!\n"); + return ret; + } + + ret = regulator_enable(data->vddio_reg); + if (ret) { + dev_err(&client->dev, "failed to enable VDDIO regulator!\n"); + goto disable_regulator_vdd; + } + + ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I); + if (ret < 0) + goto disable_regulators; + if (ret != MAG3110_DEVICE_ID) { + ret = -ENODEV; + goto disable_regulators; + } + data->client = client; mutex_init(&data->lock); @@ -499,7 +535,7 @@ static int mag3110_probe(struct i2c_client *client, ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1); if (ret < 0) - return ret; + goto disable_regulators; ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2, MAG3110_CTRL_AUTO_MRST_EN); @@ -520,16 +556,24 @@ buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); standby_on_error: mag3110_standby(iio_priv(indio_dev)); +disable_regulators: + regulator_disable(data->vddio_reg); +disable_regulator_vdd: + regulator_disable(data->vdd_reg); + return ret; } static int mag3110_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct mag3110_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); mag3110_standby(iio_priv(indio_dev)); + regulator_disable(data->vddio_reg); + regulator_disable(data->vdd_reg); return 0; } @@ -537,14 +581,48 @@ static int mag3110_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int mag3110_suspend(struct device *dev) { - return mag3110_standby(iio_priv(i2c_get_clientdata( + struct mag3110_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + int ret; + + ret = mag3110_standby(iio_priv(i2c_get_clientdata( to_i2c_client(dev)))); + if (ret) + return ret; + + ret = regulator_disable(data->vddio_reg); + if (ret) { + dev_err(dev, "failed to disable VDDIO regulator\n"); + return ret; + } + + ret = regulator_disable(data->vdd_reg); + if (ret) { + dev_err(dev, "failed to disable VDD regulator\n"); + return ret; + } + + return 0; } static int mag3110_resume(struct device *dev) { struct mag3110_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); + int ret; + + ret = regulator_enable(data->vdd_reg); + if (ret) { + dev_err(dev, "failed to enable VDD regulator\n"); + return ret; + } + + ret = regulator_enable(data->vddio_reg); + if (ret) { + dev_err(dev, "failed to enable VDDIO regulator\n"); + regulator_disable(data->vdd_reg); + return ret; + } return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, data->ctrl_reg1); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig @@ -165,7 +165,7 @@ config IIO_ST_PRESS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics pressure - sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB. + sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH. This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h @@ -21,6 +21,7 @@ enum st_press_type { LPS22HB, LPS33HW, LPS35HW, + LPS22HH, ST_PRESS_MAX, }; @@ -30,6 +31,7 @@ enum st_press_type { #define LPS22HB_PRESS_DEV_NAME "lps22hb" #define LPS33HW_PRESS_DEV_NAME "lps33hw" #define LPS35HW_PRESS_DEV_NAME "lps35hw" +#define LPS22HH_PRESS_DEV_NAME "lps22hh" /** * struct st_sensors_platform_data - default press platform data diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c @@ -492,6 +492,75 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .multi_read_bit = false, .bootime = 2, }, + { + /* + * CUSTOM VALUES FOR LPS22HH SENSOR + * See LPS22HH datasheet: + * http://www2.st.com/resource/en/datasheet/lps22hh.pdf + */ + .wai = 0xb3, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LPS22HH_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, + .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), + .odr = { + .addr = 0x10, + .mask = 0x70, + .odr_avl = { + { .hz = 1, .value = 0x01 }, + { .hz = 10, .value = 0x02 }, + { .hz = 25, .value = 0x03 }, + { .hz = 50, .value = 0x04 }, + { .hz = 75, .value = 0x05 }, + { .hz = 100, .value = 0x06 }, + { .hz = 200, .value = 0x07 }, + }, + }, + .pw = { + .addr = 0x10, + .mask = 0x70, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .fs_avl = { + /* + * Pressure and temperature sensitivity values + * as defined in table 3 of LPS22HH datasheet. + */ + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .gain = ST_PRESS_KPASCAL_NANO_SCALE, + .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS, + }, + }, + }, + .bdu = { + .addr = 0x10, + .mask = BIT(1), + }, + .drdy_irq = { + .int1 = { + .addr = 0x12, + .mask = BIT(2), + .addr_od = 0x11, + .mask_od = BIT(5), + }, + .addr_ihl = 0x11, + .mask_ihl = BIT(6), + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x03, + }, + }, + .sim = { + .addr = 0x10, + .value = BIT(0), + }, + .multi_read_bit = false, + .bootime = 2, + }, }; static int st_press_write_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c @@ -45,6 +45,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps35hw", .data = LPS35HW_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22hh", + .data = LPS22HH_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -69,6 +73,7 @@ static const struct i2c_device_id st_press_id_table[] = { { LPS22HB_PRESS_DEV_NAME, LPS22HB }, { LPS33HW_PRESS_DEV_NAME, LPS33HW }, { LPS35HW_PRESS_DEV_NAME, LPS35HW }, + { LPS22HH_PRESS_DEV_NAME, LPS22HH }, {}, }; MODULE_DEVICE_TABLE(i2c, st_press_id_table); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c @@ -49,6 +49,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps35hw", .data = LPS35HW_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22hh", + .data = LPS22HH_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -93,6 +97,7 @@ static const struct spi_device_id st_press_id_table[] = { { LPS22HB_PRESS_DEV_NAME }, { LPS33HW_PRESS_DEV_NAME }, { LPS35HW_PRESS_DEV_NAME }, + { LPS22HH_PRESS_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_press_id_table); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig @@ -56,8 +56,6 @@ source "drivers/staging/iio/Kconfig" source "drivers/staging/sm750fb/Kconfig" -source "drivers/staging/xgifb/Kconfig" - source "drivers/staging/emxx_udc/Kconfig" source "drivers/staging/speakup/Kconfig" @@ -104,12 +102,16 @@ source "drivers/staging/pi433/Kconfig" source "drivers/staging/mt7621-pci/Kconfig" +source "drivers/staging/mt7621-pci-phy/Kconfig" + source "drivers/staging/mt7621-pinctrl/Kconfig" source "drivers/staging/mt7621-spi/Kconfig" source "drivers/staging/mt7621-dma/Kconfig" +source "drivers/staging/ralink-gdma/Kconfig" + source "drivers/staging/mt7621-mmc/Kconfig" source "drivers/staging/mt7621-eth/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_VT6656) += vt6656/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_FB_SM750) += sm750fb/ -obj-$(CONFIG_FB_XGI) += xgifb/ obj-$(CONFIG_USB_EMXX) += emxx_udc/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_MFD_NVEC) += nvec/ @@ -41,12 +40,14 @@ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ obj-$(CONFIG_PI433) += pi433/ -obj-$(CONFIG_SOC_MT7621) += mt7621-pci/ -obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/ -obj-$(CONFIG_SOC_MT7621) += mt7621-spi/ +obj-$(CONFIG_PCI_MT7621) += mt7621-pci/ +obj-$(CONFIG_PCI_MT7621_PHY) += mt7621-pci-phy/ +obj-$(CONFIG_PINCTRL_RT2880) += mt7621-pinctrl/ +obj-$(CONFIG_SPI_MT7621) += mt7621-spi/ obj-$(CONFIG_SOC_MT7621) += mt7621-dma/ -obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/ -obj-$(CONFIG_SOC_MT7621) += mt7621-eth/ +obj-$(CONFIG_DMA_RALINK) += ralink-gdma/ +obj-$(CONFIG_MTK_MMC) += mt7621-mmc/ +obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mt7621-eth/ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c @@ -75,6 +75,9 @@ struct ashmem_range { /* LRU list of unpinned pages, protected by ashmem_mutex */ static LIST_HEAD(ashmem_lru_list); +static atomic_t ashmem_shrink_inflight = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(ashmem_shrink_wait); + /* * long lru_count - The count of pages on our LRU list. * @@ -126,7 +129,8 @@ static inline bool page_range_in_range(struct ashmem_range *range, page_range_subsumes_range(range, start, end); } -static inline bool range_before_page(struct ashmem_range *range, size_t page) +static inline bool range_before_page(struct ashmem_range *range, + size_t page) { return range->pgend < page; } @@ -168,19 +172,15 @@ static inline void lru_del(struct ashmem_range *range) * @end: The ending page (inclusive) * * This function is protected by ashmem_mutex. - * - * Return: 0 if successful, or -ENOMEM if there is an error */ -static int range_alloc(struct ashmem_area *asma, - struct ashmem_range *prev_range, unsigned int purged, - size_t start, size_t end) +static void range_alloc(struct ashmem_area *asma, + struct ashmem_range *prev_range, unsigned int purged, + size_t start, size_t end, + struct ashmem_range **new_range) { - struct ashmem_range *range; - - range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL); - if (!range) - return -ENOMEM; + struct ashmem_range *range = *new_range; + *new_range = NULL; range->asma = asma; range->pgstart = start; range->pgend = end; @@ -190,8 +190,6 @@ static int range_alloc(struct ashmem_area *asma, if (range_on_lru(range)) lru_add(range); - - return 0; } /** @@ -438,7 +436,6 @@ out: static unsigned long ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { - struct ashmem_range *range, *next; unsigned long freed = 0; /* We might recurse into filesystem code, so bail out if necessary */ @@ -448,21 +445,33 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) if (!mutex_trylock(&ashmem_mutex)) return -1; - list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) { + while (!list_empty(&ashmem_lru_list)) { + struct ashmem_range *range = + list_first_entry(&ashmem_lru_list, typeof(*range), lru); loff_t start = range->pgstart * PAGE_SIZE; loff_t end = (range->pgend + 1) * PAGE_SIZE; + struct file *f = range->asma->file; - range->asma->file->f_op->fallocate(range->asma->file, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - start, end - start); + get_file(f); + atomic_inc(&ashmem_shrink_inflight); range->purged = ASHMEM_WAS_PURGED; lru_del(range); freed += range_size(range); + mutex_unlock(&ashmem_mutex); + f->f_op->fallocate(f, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + start, end - start); + fput(f); + if (atomic_dec_and_test(&ashmem_shrink_inflight)) + wake_up_all(&ashmem_shrink_wait); + if (!mutex_trylock(&ashmem_mutex)) + goto out; if (--sc->nr_to_scan <= 0) break; } mutex_unlock(&ashmem_mutex); +out: return freed; } @@ -582,7 +591,8 @@ static int get_name(struct ashmem_area *asma, void __user *name) * * Caller must hold ashmem_mutex. */ -static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend) +static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend, + struct ashmem_range **new_range) { struct ashmem_range *range, *next; int ret = ASHMEM_NOT_PURGED; @@ -635,7 +645,7 @@ static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend) * second half and adjust the first chunk's endpoint. */ range_alloc(asma, range, range->purged, - pgend + 1, range->pgend); + pgend + 1, range->pgend, new_range); range_shrink(range, range->pgstart, pgstart - 1); break; } @@ -649,7 +659,8 @@ static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend) * * Caller must hold ashmem_mutex. */ -static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend) +static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend, + struct ashmem_range **new_range) { struct ashmem_range *range, *next; unsigned int purged = ASHMEM_NOT_PURGED; @@ -675,7 +686,8 @@ restart: } } - return range_alloc(asma, range, purged, pgstart, pgend); + range_alloc(asma, range, purged, pgstart, pgend, new_range); + return 0; } /* @@ -708,11 +720,19 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, struct ashmem_pin pin; size_t pgstart, pgend; int ret = -EINVAL; + struct ashmem_range *range = NULL; if (copy_from_user(&pin, p, sizeof(pin))) return -EFAULT; + if (cmd == ASHMEM_PIN || cmd == ASHMEM_UNPIN) { + range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL); + if (!range) + return -ENOMEM; + } + mutex_lock(&ashmem_mutex); + wait_event(ashmem_shrink_wait, !atomic_read(&ashmem_shrink_inflight)); if (!asma->file) goto out_unlock; @@ -735,10 +755,10 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, switch (cmd) { case ASHMEM_PIN: - ret = ashmem_pin(asma, pgstart, pgend); + ret = ashmem_pin(asma, pgstart, pgend, &range); break; case ASHMEM_UNPIN: - ret = ashmem_unpin(asma, pgstart, pgend); + ret = ashmem_unpin(asma, pgstart, pgend, &range); break; case ASHMEM_GET_PIN_STATUS: ret = ashmem_get_pin_status(asma, pgstart, pgend); @@ -747,6 +767,8 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, out_unlock: mutex_unlock(&ashmem_mutex); + if (range) + kmem_cache_free(ashmem_range_cachep, range); return ret; } diff --git a/drivers/staging/android/ion/Makefile b/drivers/staging/android/ion/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_ION) += ion.o ion-ioctl.o ion_heap.o +obj-$(CONFIG_ION) += ion.o ion_heap.o obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_system_heap.o ion_page_pool.o obj-$(CONFIG_ION_CARVEOUT_HEAP) += ion_carveout_heap.o obj-$(CONFIG_ION_CHUNK_HEAP) += ion_chunk_heap.o diff --git a/drivers/staging/android/ion/ion-ioctl.c b/drivers/staging/android/ion/ion-ioctl.c @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2011 Google, Inc. - */ - -#include <linux/kernel.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/uaccess.h> - -#include "ion.h" - -union ion_ioctl_arg { - struct ion_allocation_data allocation; - struct ion_heap_query query; -}; - -static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg) -{ - switch (cmd) { - case ION_IOC_HEAP_QUERY: - if (arg->query.reserved0 || - arg->query.reserved1 || - arg->query.reserved2) - return -EINVAL; - break; - default: - break; - } - - return 0; -} - -/* fix up the cases where the ioctl direction bits are incorrect */ -static unsigned int ion_ioctl_dir(unsigned int cmd) -{ - switch (cmd) { - default: - return _IOC_DIR(cmd); - } -} - -long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - int ret = 0; - unsigned int dir; - union ion_ioctl_arg data; - - dir = ion_ioctl_dir(cmd); - - if (_IOC_SIZE(cmd) > sizeof(data)) - return -EINVAL; - - /* - * The copy_from_user is unconditional here for both read and write - * to do the validate. If there is no write for the ioctl, the - * buffer is cleared - */ - if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) - return -EFAULT; - - ret = validate_ioctl_arg(cmd, &data); - if (ret) { - pr_warn_once("%s: ioctl validate failed\n", __func__); - return ret; - } - - if (!(dir & _IOC_WRITE)) - memset(&data, 0, sizeof(data)); - - switch (cmd) { - case ION_IOC_ALLOC: - { - int fd; - - fd = ion_alloc(data.allocation.len, - data.allocation.heap_id_mask, - data.allocation.flags); - if (fd < 0) - return fd; - - data.allocation.fd = fd; - - break; - } - case ION_IOC_HEAP_QUERY: - ret = ion_query_heaps(&data.query); - break; - default: - return -ENOTTY; - } - - if (dir & _IOC_READ) { - if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) - return -EFAULT; - } - return ret; -} diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion.c + * ION Memory Allocator * * Copyright (C) 2011 Google, Inc. */ -#include <linux/anon_inodes.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/dma-buf.h> @@ -14,10 +13,8 @@ #include <linux/file.h> #include <linux/freezer.h> #include <linux/fs.h> -#include <linux/idr.h> #include <linux/kthread.h> #include <linux/list.h> -#include <linux/memblock.h> #include <linux/miscdevice.h> #include <linux/mm.h> #include <linux/mm_types.h> @@ -390,7 +387,7 @@ static const struct dma_buf_ops dma_buf_ops = { .unmap = ion_dma_buf_kunmap, }; -int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags) +static int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags) { struct ion_device *dev = internal_dev; struct ion_buffer *buffer = NULL; @@ -447,7 +444,7 @@ int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags) return fd; } -int ion_query_heaps(struct ion_heap_query *query) +static int ion_query_heaps(struct ion_heap_query *query) { struct ion_device *dev = internal_dev; struct ion_heap_data __user *buffer = u64_to_user_ptr(query->heaps); @@ -492,6 +489,81 @@ out: return ret; } +union ion_ioctl_arg { + struct ion_allocation_data allocation; + struct ion_heap_query query; +}; + +static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg) +{ + switch (cmd) { + case ION_IOC_HEAP_QUERY: + if (arg->query.reserved0 || + arg->query.reserved1 || + arg->query.reserved2) + return -EINVAL; + break; + default: + break; + } + + return 0; +} + +static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + union ion_ioctl_arg data; + + if (_IOC_SIZE(cmd) > sizeof(data)) + return -EINVAL; + + /* + * The copy_from_user is unconditional here for both read and write + * to do the validate. If there is no write for the ioctl, the + * buffer is cleared + */ + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + ret = validate_ioctl_arg(cmd, &data); + if (ret) { + pr_warn_once("%s: ioctl validate failed\n", __func__); + return ret; + } + + if (!(_IOC_DIR(cmd) & _IOC_WRITE)) + memset(&data, 0, sizeof(data)); + + switch (cmd) { + case ION_IOC_ALLOC: + { + int fd; + + fd = ion_alloc(data.allocation.len, + data.allocation.heap_id_mask, + data.allocation.flags); + if (fd < 0) + return fd; + + data.allocation.fd = fd; + + break; + } + case ION_IOC_HEAP_QUERY: + ret = ion_query_heaps(&data.query); + break; + default: + return -ENOTTY; + } + + if (_IOC_DIR(cmd) & _IOC_READ) { + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) + return -EFAULT; + } + return ret; +} + static const struct file_operations ion_fops = { .owner = THIS_MODULE, .unlocked_ioctl = ion_ioctl, diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * drivers/staging/android/ion/ion.h + * ION Memory Allocator kernel interface header * * Copyright (C) 2011 Google, Inc. */ @@ -22,32 +22,9 @@ #include "../uapi/ion.h" /** - * struct ion_platform_heap - defines a heap in the given platform - * @type: type of the heap from ion_heap_type enum - * @id: unique identifier for heap. When allocating higher numb ers - * will be allocated from first. At allocation these are passed - * as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS. - * @name: used for debug purposes - * @base: base address of heap in physical memory if applicable - * @size: size of the heap in bytes if applicable - * @priv: private info passed from the board file - * - * Provided by the board file. - */ -struct ion_platform_heap { - enum ion_heap_type type; - unsigned int id; - const char *name; - phys_addr_t base; - size_t size; - phys_addr_t align; - void *priv; -}; - -/** * struct ion_buffer - metadata for a particular buffer - * @ref: reference count * @node: node in the ion_device buffers tree + * @list: element in list of deferred freeable buffers * @dev: back pointer to the ion_device * @heap: back pointer to the heap the buffer came from * @flags: buffer specific flags @@ -58,7 +35,8 @@ struct ion_platform_heap { * @lock: protects the buffers cnt fields * @kmap_cnt: number of times the buffer is mapped to the kernel * @vaddr: the kernel mapping if kmap_cnt is not zero - * @sg_table: the sg table for the buffer if dmap_cnt is not zero + * @sg_table: the sg table for the buffer + * @attachments: list of devices attached to this buffer */ struct ion_buffer { union { @@ -174,12 +152,16 @@ struct ion_heap { unsigned long flags; unsigned int id; const char *name; + + /* deferred free support */ struct shrinker shrinker; struct list_head free_list; size_t free_list_size; spinlock_t free_lock; wait_queue_head_t waitqueue; struct task_struct *task; + + /* heap statistics */ u64 num_of_buffers; u64 num_of_alloc_bytes; u64 alloc_bytes_wm; @@ -205,10 +187,6 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, int ion_heap_buffer_zero(struct ion_buffer *buffer); int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot); -int ion_alloc(size_t len, - unsigned int heap_id_mask, - unsigned int flags); - /** * ion_heap_init_shrinker * @heap: the heap @@ -330,8 +308,4 @@ void ion_page_pool_free(struct ion_page_pool *pool, struct page *page); int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, int nr_to_scan); -long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); - -int ion_query_heaps(struct ion_heap_query *query); - #endif /* _ION_H */ diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion_carveout_heap.c + * ION Memory Allocator carveout heap helper * * Copyright (C) 2011 Google, Inc. */ -#include <linux/spinlock.h> + #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/genalloc.h> @@ -12,7 +12,7 @@ #include <linux/mm.h> #include <linux/scatterlist.h> #include <linux/slab.h> -#include <linux/vmalloc.h> + #include "ion.h" #define ION_CARVEOUT_ALLOCATE_FAIL -1 @@ -20,7 +20,6 @@ struct ion_carveout_heap { struct ion_heap heap; struct gen_pool *pool; - phys_addr_t base; }; static phys_addr_t ion_carveout_allocate(struct ion_heap *heap, @@ -44,6 +43,7 @@ static void ion_carveout_free(struct ion_heap *heap, phys_addr_t addr, if (addr == ION_CARVEOUT_ALLOCATE_FAIL) return; + gen_pool_free(carveout_heap->pool, addr, size); } @@ -103,17 +103,14 @@ static struct ion_heap_ops carveout_heap_ops = { .unmap_kernel = ion_heap_unmap_kernel, }; -struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) +struct ion_heap *ion_carveout_heap_create(phys_addr_t base, size_t size) { struct ion_carveout_heap *carveout_heap; int ret; struct page *page; - size_t size; - - page = pfn_to_page(PFN_DOWN(heap_data->base)); - size = heap_data->size; + page = pfn_to_page(PFN_DOWN(base)); ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL)); if (ret) return ERR_PTR(ret); @@ -127,9 +124,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) kfree(carveout_heap); return ERR_PTR(-ENOMEM); } - carveout_heap->base = heap_data->base; - gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size, - -1); + gen_pool_add(carveout_heap->pool, base, size, -1); carveout_heap->heap.ops = &carveout_heap_ops; carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT; carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; diff --git a/drivers/staging/android/ion/ion_chunk_heap.c b/drivers/staging/android/ion/ion_chunk_heap.c @@ -1,23 +1,22 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion_chunk_heap.c + * ION memory allocator chunk heap helper * * Copyright (C) 2012 Google, Inc. */ + #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/genalloc.h> -#include <linux/io.h> #include <linux/mm.h> #include <linux/scatterlist.h> #include <linux/slab.h> -#include <linux/vmalloc.h> + #include "ion.h" struct ion_chunk_heap { struct ion_heap heap; struct gen_pool *pool; - phys_addr_t base; unsigned long chunk_size; unsigned long size; unsigned long allocated; @@ -108,16 +107,13 @@ static struct ion_heap_ops chunk_heap_ops = { .unmap_kernel = ion_heap_unmap_kernel, }; -struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data) +struct ion_heap *ion_chunk_heap_create(phys_addr_t base, size_t size, size_t chunk_size) { struct ion_chunk_heap *chunk_heap; int ret; struct page *page; - size_t size; - - page = pfn_to_page(PFN_DOWN(heap_data->base)); - size = heap_data->size; + page = pfn_to_page(PFN_DOWN(base)); ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL)); if (ret) return ERR_PTR(ret); @@ -126,23 +122,21 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data) if (!chunk_heap) return ERR_PTR(-ENOMEM); - chunk_heap->chunk_size = (unsigned long)heap_data->priv; + chunk_heap->chunk_size = chunk_size; chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) + PAGE_SHIFT, -1); if (!chunk_heap->pool) { ret = -ENOMEM; goto error_gen_pool_create; } - chunk_heap->base = heap_data->base; - chunk_heap->size = heap_data->size; + chunk_heap->size = size; chunk_heap->allocated = 0; - gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1); + gen_pool_add(chunk_heap->pool, base, size, -1); chunk_heap->heap.ops = &chunk_heap_ops; chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK; chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; - pr_debug("%s: base %pa size %zu\n", __func__, - &chunk_heap->base, heap_data->size); + pr_debug("%s: base %pa size %zu\n", __func__, &base, size); return &chunk_heap->heap; @@ -150,4 +144,3 @@ error_gen_pool_create: kfree(chunk_heap); return ERR_PTR(ret); } - diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion_cma_heap.c + * ION Memory Allocator CMA heap exporter * * Copyright (C) Linaro 2012 * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson. @@ -111,10 +111,6 @@ static struct ion_heap *__ion_cma_heap_create(struct cma *cma) return ERR_PTR(-ENOMEM); cma_heap->heap.ops = &ion_cma_ops; - /* - * get device from private heaps data, later it will be - * used to make the link with reserved CMA memory - */ cma_heap->cma = cma; cma_heap->heap.type = ION_HEAP_TYPE_DMA; return &cma_heap->heap; diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion_heap.c + * ION Memory Allocator generic heap helpers * * Copyright (C) 2011 Google, Inc. */ @@ -14,6 +14,7 @@ #include <uapi/linux/sched/types.h> #include <linux/scatterlist.h> #include <linux/vmalloc.h> + #include "ion.h" void *ion_heap_map_kernel(struct ion_heap *heap, @@ -92,6 +93,7 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, if (addr >= vma->vm_end) return 0; } + return 0; } @@ -254,6 +256,7 @@ int ion_heap_init_deferred_free(struct ion_heap *heap) return PTR_ERR_OR_ZERO(heap->task); } sched_setscheduler(heap->task, SCHED_IDLE, &param); + return 0; } @@ -265,8 +268,10 @@ static unsigned long ion_heap_shrink_count(struct shrinker *shrinker, int total = 0; total = ion_heap_freelist_size(heap) / PAGE_SIZE; + if (heap->ops->shrink) total += heap->ops->shrink(heap, sc->gfp_mask, 0); + return total; } @@ -295,6 +300,7 @@ static unsigned long ion_heap_shrink_scan(struct shrinker *shrinker, if (heap->ops->shrink) freed += heap->ops->shrink(heap, sc->gfp_mask, to_scan); + return freed; } diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion_mem_pool.c + * ION Memory Allocator page pool helpers * * Copyright (C) 2011 Google, Inc. */ diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/staging/android/ion/ion_system_heap.c + * ION Memory Allocator system heap exporter * * Copyright (C) 2011 Google, Inc. */ @@ -13,6 +13,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/vmalloc.h> + #include "ion.h" #define NUM_ORDERS ARRAY_SIZE(orders) @@ -223,10 +224,10 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools) static int ion_system_heap_create_pools(struct ion_page_pool **pools) { int i; - gfp_t gfp_flags = low_order_gfp_flags; for (i = 0; i < NUM_ORDERS; i++) { struct ion_page_pool *pool; + gfp_t gfp_flags = low_order_gfp_flags; if (orders[i] > 4) gfp_flags = high_order_gfp_flags; @@ -236,6 +237,7 @@ static int ion_system_heap_create_pools(struct ion_page_pool **pools) goto err_create_pool; pools[i] = pool; } + return 0; err_create_pool: @@ -274,6 +276,7 @@ static int ion_system_heap_create(void) heap->name = "ion_system_heap"; ion_device_add_heap(heap); + return 0; } device_initcall(ion_system_heap_create); @@ -355,6 +358,7 @@ static struct ion_heap *__ion_system_contig_heap_create(void) heap->ops = &kmalloc_ops; heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG; heap->name = "ion_system_contig_heap"; + return heap; } @@ -367,7 +371,7 @@ static int ion_system_contig_heap_create(void) return PTR_ERR(heap); ion_device_add_heap(heap); + return 0; } device_initcall(ion_system_contig_heap_create); - diff --git a/drivers/staging/android/uapi/ion.h b/drivers/staging/android/uapi/ion.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * drivers/staging/android/uapi/ion.h * diff --git a/drivers/staging/android/vsoc.c b/drivers/staging/android/vsoc.c @@ -29,7 +29,6 @@ #include <linux/syscalls.h> #include <linux/uaccess.h> #include <linux/interrupt.h> -#include <linux/mutex.h> #include <linux/cdev.h> #include <linux/file.h> #include "uapi/vsoc_shm.h" diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c @@ -1605,9 +1605,8 @@ static int do_insn_ioctl(struct comedi_device *dev, unsigned int n_data = MIN_SAMPLES; int ret = 0; - if (copy_from_user(&insn, arg, sizeof(insn))) { + if (copy_from_user(&insn, arg, sizeof(insn))) return -EFAULT; - } n_data = max(n_data, insn.n); diff --git a/drivers/staging/comedi/drivers/cb_pcimdas.c b/drivers/staging/comedi/drivers/cb_pcimdas.c @@ -252,9 +252,9 @@ static int cb_pcimdas_di_insn_bits(struct comedi_device *dev, } static int cb_pcimdas_do_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { struct cb_pcimdas_private *devpriv = dev->private; diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c @@ -656,6 +656,7 @@ static int ni_660x_set_pfi_routing(struct comedi_device *dev, case NI_660X_PFI_OUTPUT_DIO: if (chan > 31) return -EINVAL; + break; default: return -EINVAL; } diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c @@ -49,116 +49,117 @@ /* defines for the PCI-DIO-32HS */ -#define Window_Address 4 /* W */ -#define Interrupt_And_Window_Status 4 /* R */ -#define IntStatus1 BIT(0) -#define IntStatus2 BIT(1) -#define WindowAddressStatus_mask 0x7c - -#define Master_DMA_And_Interrupt_Control 5 /* W */ -#define InterruptLine(x) ((x) & 3) -#define OpenInt BIT(2) -#define Group_Status 5 /* R */ -#define DataLeft BIT(0) -#define Req BIT(2) -#define StopTrig BIT(3) - -#define Group_1_Flags 6 /* R */ -#define Group_2_Flags 7 /* R */ -#define TransferReady BIT(0) -#define CountExpired BIT(1) -#define Waited BIT(5) -#define PrimaryTC BIT(6) -#define SecondaryTC BIT(7) +#define WINDOW_ADDRESS 4 /* W */ +#define INTERRUPT_AND_WINDOW_STATUS 4 /* R */ +#define INT_STATUS_1 BIT(0) +#define INT_STATUS_2 BIT(1) +#define WINDOW_ADDRESS_STATUS_MASK 0x7c + +#define MASTER_DMA_AND_INTERRUPT_CONTROL 5 /* W */ +#define INTERRUPT_LINE(x) ((x) & 3) +#define OPEN_INT BIT(2) +#define GROUP_STATUS 5 /* R */ +#define DATA_LEFT BIT(0) +#define REQ BIT(2) +#define STOP_TRIG BIT(3) + +#define GROUP_1_FLAGS 6 /* R */ +#define GROUP_2_FLAGS 7 /* R */ +#define TRANSFER_READY BIT(0) +#define COUNT_EXPIRED BIT(1) +#define WAITED BIT(5) +#define PRIMARY_TC BIT(6) +#define SECONDARY_TC BIT(7) /* #define SerialRose */ /* #define ReqRose */ /* #define Paused */ -#define Group_1_First_Clear 6 /* W */ -#define Group_2_First_Clear 7 /* W */ -#define ClearWaited BIT(3) -#define ClearPrimaryTC BIT(4) -#define ClearSecondaryTC BIT(5) -#define DMAReset BIT(6) -#define FIFOReset BIT(7) -#define ClearAll 0xf8 - -#define Group_1_FIFO 8 /* W */ -#define Group_2_FIFO 12 /* W */ - -#define Transfer_Count 20 -#define Chip_ID_D 24 -#define Chip_ID_I 25 -#define Chip_ID_O 26 -#define Chip_Version 27 -#define Port_IO(x) (28 + (x)) -#define Port_Pin_Directions(x) (32 + (x)) -#define Port_Pin_Mask(x) (36 + (x)) -#define Port_Pin_Polarities(x) (40 + (x)) - -#define Master_Clock_Routing 45 -#define RTSIClocking(x) (((x) & 3) << 4) - -#define Group_1_Second_Clear 46 /* W */ -#define Group_2_Second_Clear 47 /* W */ -#define ClearExpired BIT(0) - -#define Port_Pattern(x) (48 + (x)) - -#define Data_Path 64 -#define FIFOEnableA BIT(0) -#define FIFOEnableB BIT(1) -#define FIFOEnableC BIT(2) -#define FIFOEnableD BIT(3) -#define Funneling(x) (((x) & 3) << 4) -#define GroupDirection BIT(7) - -#define Protocol_Register_1 65 -#define OpMode Protocol_Register_1 -#define RunMode(x) ((x) & 7) -#define Numbered BIT(3) - -#define Protocol_Register_2 66 -#define ClockReg Protocol_Register_2 -#define ClockLine(x) (((x) & 3) << 5) -#define InvertStopTrig BIT(7) -#define DataLatching(x) (((x) & 3) << 5) - -#define Protocol_Register_3 67 -#define Sequence Protocol_Register_3 - -#define Protocol_Register_14 68 /* 16 bit */ -#define ClockSpeed Protocol_Register_14 - -#define Protocol_Register_4 70 -#define ReqReg Protocol_Register_4 -#define ReqConditioning(x) (((x) & 7) << 3) - -#define Protocol_Register_5 71 -#define BlockMode Protocol_Register_5 +#define GROUP_1_FIRST_CLEAR 6 /* W */ +#define GROUP_2_FIRST_CLEAR 7 /* W */ +#define CLEAR_WAITED BIT(3) +#define CLEAR_PRIMARY_TC BIT(4) +#define CLEAR_SECONDARY_TC BIT(5) +#define DMA_RESET BIT(6) +#define FIFO_RESET BIT(7) +#define CLEAR_ALL 0xf8 + +#define GROUP_1_FIFO 8 /* W */ +#define GROUP_2_FIFO 12 /* W */ + +#define TRANSFER_COUNT 20 +#define CHIP_ID_D 24 +#define CHIP_ID_I 25 +#define CHIP_ID_O 26 +#define CHIP_VERSION 27 +#define PORT_IO(x) (28 + (x)) +#define PORT_PIN_DIRECTIONS(x) (32 + (x)) +#define PORT_PIN_MASK(x) (36 + (x)) +#define PORT_PIN_POLARITIES(x) (40 + (x)) + +#define MASTER_CLOCK_ROUTING 45 +#define RTSI_CLOCKING(x) (((x) & 3) << 4) + +#define GROUP_1_SECOND_CLEAR 46 /* W */ +#define GROUP_2_SECOND_CLEAR 47 /* W */ +#define CLEAR_EXPIRED BIT(0) + +#define PORT_PATTERN(x) (48 + (x)) + +#define DATA_PATH 64 +#define FIFO_ENABLE_A BIT(0) +#define FIFO_ENABLE_B BIT(1) +#define FIFO_ENABLE_C BIT(2) +#define FIFO_ENABLE_D BIT(3) +#define FUNNELING(x) (((x) & 3) << 4) +#define GROUP_DIRECTION BIT(7) + +#define PROTOCOL_REGISTER_1 65 +#define OP_MODE PROTOCOL_REGISTER_1 +#define RUN_MODE(x) ((x) & 7) +#define NUMBERED BIT(3) + +#define PROTOCOL_REGISTER_2 66 +#define CLOCK_REG PROTOCOL_REGISTER_2 +#define CLOCK_LINE(x) (((x) & 3) << 5) +#define INVERT_STOP_TRIG BIT(7) +#define DATA_LATCHING(x) (((x) & 3) << 5) + +#define PROTOCOL_REGISTER_3 67 +#define SEQUENCE PROTOCOL_REGISTER_3 + +#define PROTOCOL_REGISTER_14 68 /* 16 bit */ +#define CLOCK_SPEED PROTOCOL_REGISTER_14 + +#define PROTOCOL_REGISTER_4 70 +#define REQ_REG PROTOCOL_REGISTER_4 +#define REQ_CONDITIONING(x) (((x) & 7) << 3) + +#define PROTOCOL_REGISTER_5 71 +#define BLOCK_MODE PROTOCOL_REGISTER_5 #define FIFO_Control 72 -#define ReadyLevel(x) ((x) & 7) - -#define Protocol_Register_6 73 -#define LinePolarities Protocol_Register_6 -#define InvertAck BIT(0) -#define InvertReq BIT(1) -#define InvertClock BIT(2) -#define InvertSerial BIT(3) -#define OpenAck BIT(4) -#define OpenClock BIT(5) - -#define Protocol_Register_7 74 -#define AckSer Protocol_Register_7 -#define AckLine(x) (((x) & 3) << 2) -#define ExchangePins BIT(7) - -#define Interrupt_Control 75 - /* bits same as flags */ - -#define DMA_Line_Control_Group1 76 -#define DMA_Line_Control_Group2 108 +#define READY_LEVEL(x) ((x) & 7) + +#define PROTOCOL_REGISTER_6 73 +#define LINE_POLARITIES PROTOCOL_REGISTER_6 +#define INVERT_ACK BIT(0) +#define INVERT_REQ BIT(1) +#define INVERT_CLOCK BIT(2) +#define INVERT_SERIAL BIT(3) +#define OPEN_ACK BIT(4) +#define OPEN_CLOCK BIT(5) + +#define PROTOCOL_REGISTER_7 74 +#define ACK_SER PROTOCOL_REGISTER_7 +#define ACK_LINE(x) (((x) & 3) << 2) +#define EXCHANGE_PINS BIT(7) + +#define INTERRUPT_CONTROL 75 +/* bits same as flags */ + +#define DMA_LINE_CONTROL_GROUP1 76 +#define DMA_LINE_CONTROL_GROUP2 108 + /* channel zero is none */ static inline unsigned int primary_DMAChannel_bits(unsigned int channel) { @@ -170,41 +171,41 @@ static inline unsigned int secondary_DMAChannel_bits(unsigned int channel) return (channel << 2) & 0xc; } -#define Transfer_Size_Control 77 -#define TransferWidth(x) ((x) & 3) -#define TransferLength(x) (((x) & 3) << 3) -#define RequireRLevel BIT(5) +#define TRANSFER_SIZE_CONTROL 77 +#define TRANSFER_WIDTH(x) ((x) & 3) +#define TRANSFER_LENGTH(x) (((x) & 3) << 3) +#define REQUIRE_R_LEVEL BIT(5) -#define Protocol_Register_15 79 -#define DAQOptions Protocol_Register_15 -#define StartSource(x) ((x) & 0x3) -#define InvertStart BIT(2) -#define StopSource(x) (((x) & 0x3) << 3) -#define ReqStart BIT(6) -#define PreStart BIT(7) +#define PROTOCOL_REGISTER_15 79 +#define DAQ_OPTIONS PROTOCOL_REGISTER_15 +#define START_SOURCE(x) ((x) & 0x3) +#define INVERT_START BIT(2) +#define STOP_SOURCE(x) (((x) & 0x3) << 3) +#define REQ_START BIT(6) +#define PRE_START BIT(7) -#define Pattern_Detection 81 -#define DetectionMethod BIT(0) -#define InvertMatch BIT(1) -#define IE_Pattern_Detection BIT(2) +#define PATTERN_DETECTION 81 +#define DETECTION_METHOD BIT(0) +#define INVERT_MATCH BIT(1) +#define IE_PATTERN_DETECTION BIT(2) -#define Protocol_Register_9 82 -#define ReqDelay Protocol_Register_9 +#define PROTOCOL_REGISTER_9 82 +#define REQ_DELAY PROTOCOL_REGISTER_9 -#define Protocol_Register_10 83 -#define ReqNotDelay Protocol_Register_10 +#define PROTOCOL_REGISTER_10 83 +#define REQ_NOT_DELAY PROTOCOL_REGISTER_10 -#define Protocol_Register_11 84 -#define AckDelay Protocol_Register_11 +#define PROTOCOL_REGISTER_11 84 +#define ACK_DELAY PROTOCOL_REGISTER_11 -#define Protocol_Register_12 85 -#define AckNotDelay Protocol_Register_12 +#define PROTOCOL_REGISTER_12 85 +#define ACK_NOT_DELAY PROTOCOL_REGISTER_12 -#define Protocol_Register_13 86 -#define Data1Delay Protocol_Register_13 +#define PROTOCOL_REGISTER_13 86 +#define DATA_1_DELAY PROTOCOL_REGISTER_13 -#define Protocol_Register_8 88 /* 32 bit */ -#define StartDelay Protocol_Register_8 +#define PROTOCOL_REGISTER_8 88 /* 32 bit */ +#define START_DELAY PROTOCOL_REGISTER_8 /* Firmware files for PCI-6524 */ #define FW_PCI_6534_MAIN "ni6534a.bin" @@ -246,9 +247,10 @@ enum FPGA_Control_Bits { #define TIMER_BASE 50 /* nanoseconds */ #ifdef USE_DMA -#define IntEn (CountExpired | Waited | PrimaryTC | SecondaryTC) +#define INT_EN (COUNT_EXPIRED | WAITED | PRIMARY_TC | SECONDARY_TC) #else -#define IntEn (TransferReady | CountExpired | Waited | PrimaryTC | SecondaryTC) +#define INT_EN (TRANSFER_READY | COUNT_EXPIRED | WAITED \ + | PRIMARY_TC | SECONDARY_TC) #endif enum nidio_boardid { @@ -283,7 +285,7 @@ struct nidio96_private { struct mite *mite; int boardtype; int dio; - unsigned short OpModeBits; + unsigned short OP_MODEBits; struct mite_channel *di_mite_chan; struct mite_ring *di_mite_ring; spinlock_t mite_channel_lock; @@ -307,7 +309,7 @@ static int ni_pcidio_request_di_mite_channel(struct comedi_device *dev) devpriv->di_mite_chan->dir = COMEDI_INPUT; writeb(primary_DMAChannel_bits(devpriv->di_mite_chan->channel) | secondary_DMAChannel_bits(devpriv->di_mite_chan->channel), - dev->mmio + DMA_Line_Control_Group1); + dev->mmio + DMA_LINE_CONTROL_GROUP1); mmiowb(); spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); return 0; @@ -324,7 +326,7 @@ static void ni_pcidio_release_di_mite_channel(struct comedi_device *dev) devpriv->di_mite_chan = NULL; writeb(primary_DMAChannel_bits(0) | secondary_DMAChannel_bits(0), - dev->mmio + DMA_Line_Control_Group1); + dev->mmio + DMA_LINE_CONTROL_GROUP1); mmiowb(); } spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); @@ -391,8 +393,8 @@ static irqreturn_t nidio_interrupt(int irq, void *d) /* Lock to avoid race with comedi_poll */ spin_lock(&dev->spinlock); - status = readb(dev->mmio + Interrupt_And_Window_Status); - flags = readb(dev->mmio + Group_1_Flags); + status = readb(dev->mmio + INTERRUPT_AND_WINDOW_STATUS); + flags = readb(dev->mmio + GROUP_1_FLAGS); spin_lock(&devpriv->mite_channel_lock); if (devpriv->di_mite_chan) { @@ -401,63 +403,63 @@ static irqreturn_t nidio_interrupt(int irq, void *d) } spin_unlock(&devpriv->mite_channel_lock); - while (status & DataLeft) { + while (status & DATA_LEFT) { work++; if (work > 20) { dev_dbg(dev->class_dev, "too much work in interrupt\n"); writeb(0x00, - dev->mmio + Master_DMA_And_Interrupt_Control); + dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL); break; } - flags &= IntEn; + flags &= INT_EN; - if (flags & TransferReady) { - while (flags & TransferReady) { + if (flags & TRANSFER_READY) { + while (flags & TRANSFER_READY) { work++; if (work > 100) { dev_dbg(dev->class_dev, "too much work in interrupt\n"); writeb(0x00, dev->mmio + - Master_DMA_And_Interrupt_Control + MASTER_DMA_AND_INTERRUPT_CONTROL ); goto out; } - auxdata = readl(dev->mmio + Group_1_FIFO); + auxdata = readl(dev->mmio + GROUP_1_FIFO); comedi_buf_write_samples(s, &auxdata, 1); - flags = readb(dev->mmio + Group_1_Flags); + flags = readb(dev->mmio + GROUP_1_FLAGS); } } - if (flags & CountExpired) { - writeb(ClearExpired, dev->mmio + Group_1_Second_Clear); + if (flags & COUNT_EXPIRED) { + writeb(CLEAR_EXPIRED, dev->mmio + GROUP_1_SECOND_CLEAR); async->events |= COMEDI_CB_EOA; - writeb(0x00, dev->mmio + OpMode); + writeb(0x00, dev->mmio + OP_MODE); break; - } else if (flags & Waited) { - writeb(ClearWaited, dev->mmio + Group_1_First_Clear); + } else if (flags & WAITED) { + writeb(CLEAR_WAITED, dev->mmio + GROUP_1_FIRST_CLEAR); async->events |= COMEDI_CB_ERROR; break; - } else if (flags & PrimaryTC) { - writeb(ClearPrimaryTC, - dev->mmio + Group_1_First_Clear); + } else if (flags & PRIMARY_TC) { + writeb(CLEAR_PRIMARY_TC, + dev->mmio + GROUP_1_FIRST_CLEAR); async->events |= COMEDI_CB_EOA; - } else if (flags & SecondaryTC) { - writeb(ClearSecondaryTC, - dev->mmio + Group_1_First_Clear); + } else if (flags & SECONDARY_TC) { + writeb(CLEAR_SECONDARY_TC, + dev->mmio + GROUP_1_FIRST_CLEAR); async->events |= COMEDI_CB_EOA; } - flags = readb(dev->mmio + Group_1_Flags); - status = readb(dev->mmio + Interrupt_And_Window_Status); + flags = readb(dev->mmio + GROUP_1_FLAGS); + status = readb(dev->mmio + INTERRUPT_AND_WINDOW_STATUS); } out: comedi_handle_events(dev, s); #if 0 if (!tag) - writeb(0x03, dev->mmio + Master_DMA_And_Interrupt_Control); + writeb(0x03, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL); #endif spin_unlock(&dev->spinlock); @@ -484,7 +486,7 @@ static int ni_pcidio_insn_config(struct comedi_device *dev, if (ret) return ret; - writel(s->io_bits, dev->mmio + Port_Pin_Directions(0)); + writel(s->io_bits, dev->mmio + PORT_PIN_DIRECTIONS(0)); return insn->n; } @@ -495,9 +497,9 @@ static int ni_pcidio_insn_bits(struct comedi_device *dev, unsigned int *data) { if (comedi_dio_update_state(s, data)) - writel(s->state, dev->mmio + Port_IO(0)); + writel(s->state, dev->mmio + PORT_IO(0)); - data[1] = readl(dev->mmio + Port_IO(0)); + data[1] = readl(dev->mmio + PORT_IO(0)); return insn->n; } @@ -609,7 +611,7 @@ static int ni_pcidio_inttrig(struct comedi_device *dev, if (trig_num != cmd->start_arg) return -EINVAL; - writeb(devpriv->OpModeBits, dev->mmio + OpMode); + writeb(devpriv->OP_MODEBits, dev->mmio + OP_MODE); s->async->inttrig = NULL; return 1; @@ -621,78 +623,78 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) struct comedi_cmd *cmd = &s->async->cmd; /* XXX configure ports for input */ - writel(0x0000, dev->mmio + Port_Pin_Directions(0)); + writel(0x0000, dev->mmio + PORT_PIN_DIRECTIONS(0)); if (1) { /* enable fifos A B C D */ - writeb(0x0f, dev->mmio + Data_Path); + writeb(0x0f, dev->mmio + DATA_PATH); /* set transfer width a 32 bits */ - writeb(TransferWidth(0) | TransferLength(0), - dev->mmio + Transfer_Size_Control); + writeb(TRANSFER_WIDTH(0) | TRANSFER_LENGTH(0), + dev->mmio + TRANSFER_SIZE_CONTROL); } else { - writeb(0x03, dev->mmio + Data_Path); - writeb(TransferWidth(3) | TransferLength(0), - dev->mmio + Transfer_Size_Control); + writeb(0x03, dev->mmio + DATA_PATH); + writeb(TRANSFER_WIDTH(3) | TRANSFER_LENGTH(0), + dev->mmio + TRANSFER_SIZE_CONTROL); } /* protocol configuration */ if (cmd->scan_begin_src == TRIG_TIMER) { /* page 4-5, "input with internal REQs" */ - writeb(0, dev->mmio + OpMode); - writeb(0x00, dev->mmio + ClockReg); - writeb(1, dev->mmio + Sequence); - writeb(0x04, dev->mmio + ReqReg); - writeb(4, dev->mmio + BlockMode); - writeb(3, dev->mmio + LinePolarities); - writeb(0xc0, dev->mmio + AckSer); + writeb(0, dev->mmio + OP_MODE); + writeb(0x00, dev->mmio + CLOCK_REG); + writeb(1, dev->mmio + SEQUENCE); + writeb(0x04, dev->mmio + REQ_REG); + writeb(4, dev->mmio + BLOCK_MODE); + writeb(3, dev->mmio + LINE_POLARITIES); + writeb(0xc0, dev->mmio + ACK_SER); writel(ni_pcidio_ns_to_timer(&cmd->scan_begin_arg, CMDF_ROUND_NEAREST), - dev->mmio + StartDelay); - writeb(1, dev->mmio + ReqDelay); - writeb(1, dev->mmio + ReqNotDelay); - writeb(1, dev->mmio + AckDelay); - writeb(0x0b, dev->mmio + AckNotDelay); - writeb(0x01, dev->mmio + Data1Delay); + dev->mmio + START_DELAY); + writeb(1, dev->mmio + REQ_DELAY); + writeb(1, dev->mmio + REQ_NOT_DELAY); + writeb(1, dev->mmio + ACK_DELAY); + writeb(0x0b, dev->mmio + ACK_NOT_DELAY); + writeb(0x01, dev->mmio + DATA_1_DELAY); /* * manual, page 4-5: - * ClockSpeed comment is incorrectly listed on DAQOptions + * CLOCK_SPEED comment is incorrectly listed on DAQ_OPTIONS */ - writew(0, dev->mmio + ClockSpeed); - writeb(0, dev->mmio + DAQOptions); + writew(0, dev->mmio + CLOCK_SPEED); + writeb(0, dev->mmio + DAQ_OPTIONS); } else { /* TRIG_EXT */ /* page 4-5, "input with external REQs" */ - writeb(0, dev->mmio + OpMode); - writeb(0x00, dev->mmio + ClockReg); - writeb(0, dev->mmio + Sequence); - writeb(0x00, dev->mmio + ReqReg); - writeb(4, dev->mmio + BlockMode); + writeb(0, dev->mmio + OP_MODE); + writeb(0x00, dev->mmio + CLOCK_REG); + writeb(0, dev->mmio + SEQUENCE); + writeb(0x00, dev->mmio + REQ_REG); + writeb(4, dev->mmio + BLOCK_MODE); if (!(cmd->scan_begin_arg & CR_INVERT)) /* Leading Edge */ - writeb(0, dev->mmio + LinePolarities); + writeb(0, dev->mmio + LINE_POLARITIES); else /* Trailing Edge */ - writeb(2, dev->mmio + LinePolarities); - writeb(0x00, dev->mmio + AckSer); - writel(1, dev->mmio + StartDelay); - writeb(1, dev->mmio + ReqDelay); - writeb(1, dev->mmio + ReqNotDelay); - writeb(1, dev->mmio + AckDelay); - writeb(0x0C, dev->mmio + AckNotDelay); - writeb(0x10, dev->mmio + Data1Delay); - writew(0, dev->mmio + ClockSpeed); - writeb(0x60, dev->mmio + DAQOptions); + writeb(2, dev->mmio + LINE_POLARITIES); + writeb(0x00, dev->mmio + ACK_SER); + writel(1, dev->mmio + START_DELAY); + writeb(1, dev->mmio + REQ_DELAY); + writeb(1, dev->mmio + REQ_NOT_DELAY); + writeb(1, dev->mmio + ACK_DELAY); + writeb(0x0C, dev->mmio + ACK_NOT_DELAY); + writeb(0x10, dev->mmio + DATA_1_DELAY); + writew(0, dev->mmio + CLOCK_SPEED); + writeb(0x60, dev->mmio + DAQ_OPTIONS); } if (cmd->stop_src == TRIG_COUNT) { writel(cmd->stop_arg, - dev->mmio + Transfer_Count); + dev->mmio + TRANSFER_COUNT); } else { /* XXX */ } #ifdef USE_DMA - writeb(ClearPrimaryTC | ClearSecondaryTC, - dev->mmio + Group_1_First_Clear); + writeb(CLEAR_PRIMARY_TC | CLEAR_SECONDARY_TC, + dev->mmio + GROUP_1_FIRST_CLEAR); { int retval = setup_mite_dma(dev, s); @@ -701,25 +703,25 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) return retval; } #else - writeb(0x00, dev->mmio + DMA_Line_Control_Group1); + writeb(0x00, dev->mmio + DMA_LINE_CONTROL_GROUP1); #endif - writeb(0x00, dev->mmio + DMA_Line_Control_Group2); + writeb(0x00, dev->mmio + DMA_LINE_CONTROL_GROUP2); /* clear and enable interrupts */ - writeb(0xff, dev->mmio + Group_1_First_Clear); - /* writeb(ClearExpired, dev->mmio+Group_1_Second_Clear); */ + writeb(0xff, dev->mmio + GROUP_1_FIRST_CLEAR); + /* writeb(CLEAR_EXPIRED, dev->mmio+GROUP_1_SECOND_CLEAR); */ - writeb(IntEn, dev->mmio + Interrupt_Control); - writeb(0x03, dev->mmio + Master_DMA_And_Interrupt_Control); + writeb(INT_EN, dev->mmio + INTERRUPT_CONTROL); + writeb(0x03, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL); if (cmd->stop_src == TRIG_NONE) { - devpriv->OpModeBits = DataLatching(0) | RunMode(7); + devpriv->OP_MODEBits = DATA_LATCHING(0) | RUN_MODE(7); } else { /* TRIG_TIMER */ - devpriv->OpModeBits = Numbered | RunMode(7); + devpriv->OP_MODEBits = NUMBERED | RUN_MODE(7); } if (cmd->start_src == TRIG_NOW) { /* start */ - writeb(devpriv->OpModeBits, dev->mmio + OpMode); + writeb(devpriv->OP_MODEBits, dev->mmio + OP_MODE); s->async->inttrig = NULL; } else { /* TRIG_INT */ @@ -732,7 +734,7 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) static int ni_pcidio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - writeb(0x00, dev->mmio + Master_DMA_And_Interrupt_Control); + writeb(0x00, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL); ni_pcidio_release_di_mite_channel(dev); return 0; @@ -869,12 +871,12 @@ static int pci_6534_upload_firmware(struct comedi_device *dev) static void nidio_reset_board(struct comedi_device *dev) { - writel(0, dev->mmio + Port_IO(0)); - writel(0, dev->mmio + Port_Pin_Directions(0)); - writel(0, dev->mmio + Port_Pin_Mask(0)); + writel(0, dev->mmio + PORT_IO(0)); + writel(0, dev->mmio + PORT_PIN_DIRECTIONS(0)); + writel(0, dev->mmio + PORT_PIN_MASK(0)); /* disable interrupts on board */ - writeb(0, dev->mmio + Master_DMA_And_Interrupt_Control); + writeb(0, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL); } static int nidio_auto_attach(struct comedi_device *dev, @@ -925,7 +927,7 @@ static int nidio_auto_attach(struct comedi_device *dev, return ret; dev_info(dev->class_dev, "%s rev=%d\n", dev->board_name, - readb(dev->mmio + Chip_Version)); + readb(dev->mmio + CHIP_VERSION)); s = &dev->subdevices[0]; diff --git a/drivers/staging/comedi/drivers/ni_tio.c b/drivers/staging/comedi/drivers/ni_tio.c @@ -224,13 +224,16 @@ static void ni_tio_set_bits_transient(struct ni_gpct *counter, unsigned int transient) { struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned int chip = counter->chip_index; unsigned long flags; - if (reg < NITIO_NUM_REGS) { + if (reg < NITIO_NUM_REGS && chip < counter_dev->num_chips) { + unsigned int *regs = counter_dev->regs[chip]; + spin_lock_irqsave(&counter_dev->regs_lock, flags); - counter_dev->regs[reg] &= ~mask; - counter_dev->regs[reg] |= (value & mask); - ni_tio_write(counter, counter_dev->regs[reg] | transient, reg); + regs[reg] &= ~mask; + regs[reg] |= (value & mask); + ni_tio_write(counter, regs[reg] | transient, reg); mmiowb(); spin_unlock_irqrestore(&counter_dev->regs_lock, flags); } @@ -267,12 +270,13 @@ unsigned int ni_tio_get_soft_copy(const struct ni_gpct *counter, enum ni_gpct_register reg) { struct ni_gpct_device *counter_dev = counter->counter_dev; + unsigned int chip = counter->chip_index; unsigned int value = 0; unsigned long flags; - if (reg < NITIO_NUM_REGS) { + if (reg < NITIO_NUM_REGS && chip < counter_dev->num_chips) { spin_lock_irqsave(&counter_dev->regs_lock, flags); - value = counter_dev->regs[reg]; + value = counter_dev->regs[chip][reg]; spin_unlock_irqrestore(&counter_dev->regs_lock, flags); } return value; @@ -302,6 +306,7 @@ static int ni_m_series_clock_src_select(const struct ni_gpct *counter, { struct ni_gpct_device *counter_dev = counter->counter_dev; unsigned int cidx = counter->counter_index; + unsigned int chip = counter->chip_index; unsigned int second_gate_reg = NITIO_GATE2_REG(cidx); unsigned int clock_source = 0; unsigned int src; @@ -318,7 +323,7 @@ static int ni_m_series_clock_src_select(const struct ni_gpct *counter, clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS; break; case NI_M_TIMEBASE_3_CLK: - if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL) + if (counter_dev->regs[chip][second_gate_reg] & GI_SRC_SUBSEL) clock_source = NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS; else @@ -328,7 +333,7 @@ static int ni_m_series_clock_src_select(const struct ni_gpct *counter, clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS; break; case NI_M_NEXT_GATE_CLK: - if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL) + if (counter_dev->regs[chip][second_gate_reg] & GI_SRC_SUBSEL) clock_source = NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS; else clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS; @@ -721,6 +726,7 @@ static void ni_tio_set_source_subselect(struct ni_gpct *counter, { struct ni_gpct_device *counter_dev = counter->counter_dev; unsigned int cidx = counter->counter_index; + unsigned int chip = counter->chip_index; unsigned int second_gate_reg = NITIO_GATE2_REG(cidx); if (counter_dev->variant != ni_gpct_variant_m_series) @@ -729,18 +735,18 @@ static void ni_tio_set_source_subselect(struct ni_gpct *counter, /* Gi_Source_Subselect is zero */ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS: case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS: - counter_dev->regs[second_gate_reg] &= ~GI_SRC_SUBSEL; + counter_dev->regs[chip][second_gate_reg] &= ~GI_SRC_SUBSEL; break; /* Gi_Source_Subselect is one */ case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS: case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS: - counter_dev->regs[second_gate_reg] |= GI_SRC_SUBSEL; + counter_dev->regs[chip][second_gate_reg] |= GI_SRC_SUBSEL; break; /* Gi_Source_Subselect doesn't matter */ default: return; } - ni_tio_write(counter, counter_dev->regs[second_gate_reg], + ni_tio_write(counter, counter_dev->regs[chip][second_gate_reg], second_gate_reg); } @@ -1116,6 +1122,7 @@ static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned int index, { struct ni_gpct_device *counter_dev = counter->counter_dev; unsigned int cidx = counter->counter_index; + unsigned int chip = counter->chip_index; unsigned int abz_reg, shift, mask; if (counter_dev->variant != ni_gpct_variant_m_series) @@ -1141,9 +1148,9 @@ static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned int index, if (source > 0x1f) source = 0x1f; /* Disable gate */ - counter_dev->regs[abz_reg] &= ~mask; - counter_dev->regs[abz_reg] |= (source << shift) & mask; - ni_tio_write(counter, counter_dev->regs[abz_reg], abz_reg); + counter_dev->regs[chip][abz_reg] &= ~mask; + counter_dev->regs[chip][abz_reg] |= (source << shift) & mask; + ni_tio_write(counter, counter_dev->regs[chip][abz_reg], abz_reg); return 0; } @@ -1632,6 +1639,7 @@ int ni_tio_insn_read(struct comedi_device *dev, struct ni_gpct_device *counter_dev = counter->counter_dev; unsigned int channel = CR_CHAN(insn->chanspec); unsigned int cidx = counter->counter_index; + unsigned int chip = counter->chip_index; int i; for (i = 0; i < insn->n; i++) { @@ -1640,10 +1648,12 @@ int ni_tio_insn_read(struct comedi_device *dev, data[i] = ni_tio_read_sw_save_reg(dev, s); break; case 1: - data[i] = counter_dev->regs[NITIO_LOADA_REG(cidx)]; + data[i] = + counter_dev->regs[chip][NITIO_LOADA_REG(cidx)]; break; case 2: - data[i] = counter_dev->regs[NITIO_LOADB_REG(cidx)]; + data[i] = + counter_dev->regs[chip][NITIO_LOADB_REG(cidx)]; break; } } @@ -1670,6 +1680,7 @@ int ni_tio_insn_write(struct comedi_device *dev, struct ni_gpct_device *counter_dev = counter->counter_dev; unsigned int channel = CR_CHAN(insn->chanspec); unsigned int cidx = counter->counter_index; + unsigned int chip = counter->chip_index; unsigned int load_reg; if (insn->n < 1) @@ -1690,14 +1701,15 @@ int ni_tio_insn_write(struct comedi_device *dev, ni_tio_set_bits_transient(counter, NITIO_CMD_REG(cidx), 0, 0, GI_LOAD); /* restore load reg */ - ni_tio_write(counter, counter_dev->regs[load_reg], load_reg); + ni_tio_write(counter, counter_dev->regs[chip][load_reg], + load_reg); break; case 1: - counter_dev->regs[NITIO_LOADA_REG(cidx)] = data[0]; + counter_dev->regs[chip][NITIO_LOADA_REG(cidx)] = data[0]; ni_tio_write(counter, data[0], NITIO_LOADA_REG(cidx)); break; case 2: - counter_dev->regs[NITIO_LOADB_REG(cidx)] = data[0]; + counter_dev->regs[chip][NITIO_LOADB_REG(cidx)] = data[0]; ni_tio_write(counter, data[0], NITIO_LOADB_REG(cidx)); break; default: @@ -1711,11 +1723,12 @@ void ni_tio_init_counter(struct ni_gpct *counter) { struct ni_gpct_device *counter_dev = counter->counter_dev; unsigned int cidx = counter->counter_index; + unsigned int chip = counter->chip_index; ni_tio_reset_count_and_disarm(counter); /* initialize counter registers */ - counter_dev->regs[NITIO_AUTO_INC_REG(cidx)] = 0x0; + counter_dev->regs[chip][NITIO_AUTO_INC_REG(cidx)] = 0x0; ni_tio_write(counter, 0x0, NITIO_AUTO_INC_REG(cidx)); ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), @@ -1723,10 +1736,10 @@ void ni_tio_init_counter(struct ni_gpct *counter) ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), ~0, 0); - counter_dev->regs[NITIO_LOADA_REG(cidx)] = 0x0; + counter_dev->regs[chip][NITIO_LOADA_REG(cidx)] = 0x0; ni_tio_write(counter, 0x0, NITIO_LOADA_REG(cidx)); - counter_dev->regs[NITIO_LOADB_REG(cidx)] = 0x0; + counter_dev->regs[chip][NITIO_LOADB_REG(cidx)] = 0x0; ni_tio_write(counter, 0x0, NITIO_LOADB_REG(cidx)); ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), ~0, 0); @@ -1735,7 +1748,7 @@ void ni_tio_init_counter(struct ni_gpct *counter) ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), ~0, 0); if (ni_tio_has_gate2_registers(counter_dev)) { - counter_dev->regs[NITIO_GATE2_REG(cidx)] = 0x0; + counter_dev->regs[chip][NITIO_GATE2_REG(cidx)] = 0x0; ni_tio_write(counter, 0x0, NITIO_GATE2_REG(cidx)); } @@ -1776,9 +1789,16 @@ ni_gpct_device_construct(struct comedi_device *dev, spin_lock_init(&counter_dev->regs_lock); + counter_dev->num_counters = num_counters; + counter_dev->num_chips = DIV_ROUND_UP(num_counters, counters_per_chip); + counter_dev->counters = kcalloc(num_counters, sizeof(*counter), GFP_KERNEL); - if (!counter_dev->counters) { + counter_dev->regs = kcalloc(counter_dev->num_chips, + sizeof(*counter_dev->regs), GFP_KERNEL); + if (!counter_dev->regs || !counter_dev->counters) { + kfree(counter_dev->regs); + kfree(counter_dev->counters); kfree(counter_dev); return NULL; } @@ -1790,8 +1810,6 @@ ni_gpct_device_construct(struct comedi_device *dev, counter->counter_index = i % counters_per_chip; spin_lock_init(&counter->lock); } - counter_dev->num_counters = num_counters; - counter_dev->counters_per_chip = counters_per_chip; return counter_dev; } @@ -1801,6 +1819,7 @@ void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev) { if (!counter_dev) return; + kfree(counter_dev->regs); kfree(counter_dev->counters); kfree(counter_dev); } diff --git a/drivers/staging/comedi/drivers/ni_tio.h b/drivers/staging/comedi/drivers/ni_tio.h @@ -107,8 +107,8 @@ struct ni_gpct_device { enum ni_gpct_variant variant; struct ni_gpct *counters; unsigned int num_counters; - unsigned int counters_per_chip; - unsigned int regs[NITIO_NUM_REGS]; + unsigned int num_chips; + unsigned int (*regs)[NITIO_NUM_REGS]; /* [num_chips][NITIO_NUM_REGS] */ spinlock_t regs_lock; /* protects 'regs' */ const struct ni_route_tables *routing_tables; /* link to routes */ }; diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c @@ -61,7 +61,7 @@ #define USBDUXFASTSUB_CPUCS 0xE600 /* - * max lenghth of the transfer-buffer for software upload + * max length of the transfer-buffer for software upload */ #define TB_LEN 0x2000 diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c @@ -27,7 +27,7 @@ #include <linux/usb/gadget.h> #include <linux/irq.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include "emxx_udc.h" @@ -2220,7 +2220,7 @@ static inline void _nbu2ss_check_vbus(struct nbu2ss_udc *udc) mdelay(VBUS_CHATTERING_MDELAY); /* wait (ms) */ /* VBUS ON Check*/ - reg_dt = gpio_get_value(VBUS_VALUE); + reg_dt = gpiod_get_value(vbus_gpio); if (reg_dt == 0) { udc->linux_suspended = 0; @@ -2247,7 +2247,7 @@ static inline void _nbu2ss_check_vbus(struct nbu2ss_udc *udc) } } else { mdelay(5); /* wait (5ms) */ - reg_dt = gpio_get_value(VBUS_VALUE); + reg_dt = gpiod_get_value(vbus_gpio); if (reg_dt == 0) return; @@ -2311,7 +2311,7 @@ static inline void _nbu2ss_int_usb_suspend(struct nbu2ss_udc *udc) u32 reg_dt; if (udc->usb_suspended == 0) { - reg_dt = gpio_get_value(VBUS_VALUE); + reg_dt = gpiod_get_value(vbus_gpio); if (reg_dt == 0) return; @@ -2351,7 +2351,7 @@ static irqreturn_t _nbu2ss_udc_irq(int irq, void *_udc) struct nbu2ss_udc *udc = (struct nbu2ss_udc *)_udc; struct fc_regs __iomem *preg = udc->p_regs; - if (gpio_get_value(VBUS_VALUE) == 0) { + if (gpiod_get_value(vbus_gpio) == 0) { _nbu2ss_writel(&preg->USB_INT_STA, ~USB_INT_STA_RW); _nbu2ss_writel(&preg->USB_INT_ENA, 0); return IRQ_HANDLED; @@ -2360,7 +2360,7 @@ static irqreturn_t _nbu2ss_udc_irq(int irq, void *_udc) spin_lock(&udc->lock); for (;;) { - if (gpio_get_value(VBUS_VALUE) == 0) { + if (gpiod_get_value(vbus_gpio) == 0) { _nbu2ss_writel(&preg->USB_INT_STA, ~USB_INT_STA_RW); _nbu2ss_writel(&preg->USB_INT_ENA, 0); status = 0; @@ -2750,7 +2750,7 @@ static int nbu2ss_ep_fifo_status(struct usb_ep *_ep) preg = udc->p_regs; - data = gpio_get_value(VBUS_VALUE); + data = gpiod_get_value(vbus_gpio); if (data == 0) return -EINVAL; @@ -2790,7 +2790,7 @@ static void nbu2ss_ep_fifo_flush(struct usb_ep *_ep) return; } - data = gpio_get_value(VBUS_VALUE); + data = gpiod_get_value(vbus_gpio); if (data == 0) return; @@ -2832,7 +2832,7 @@ static int nbu2ss_gad_get_frame(struct usb_gadget *pgadget) } udc = container_of(pgadget, struct nbu2ss_udc, gadget); - data = gpio_get_value(VBUS_VALUE); + data = gpiod_get_value(vbus_gpio); if (data == 0) return -EINVAL; @@ -2854,7 +2854,7 @@ static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget) udc = container_of(pgadget, struct nbu2ss_udc, gadget); - data = gpio_get_value(VBUS_VALUE); + data = gpiod_get_value(vbus_gpio); if (data == 0) { dev_warn(&pgadget->dev, "VBUS LEVEL = %d\n", data); return -EINVAL; @@ -3119,12 +3119,13 @@ static int nbu2ss_drv_probe(struct platform_device *pdev) } /* VBUS Interrupt */ - irq_set_irq_type(INT_VBUS, IRQ_TYPE_EDGE_BOTH); - status = request_irq(INT_VBUS, + vbus_irq = gpiod_to_irq(vbus_gpio); + irq_set_irq_type(vbus_irq, IRQ_TYPE_EDGE_BOTH); + status = request_irq(vbus_irq, _nbu2ss_vbus_irq, IRQF_SHARED, driver_name, udc); if (status != 0) { - dev_err(udc->dev, "request_irq(INT_VBUS) failed\n"); + dev_err(udc->dev, "request_irq(vbus_irq) failed\n"); return status; } @@ -3160,7 +3161,7 @@ static int nbu2ss_drv_remove(struct platform_device *pdev) } /* Interrupt Handler - Release */ - free_irq(INT_VBUS, udc); + free_irq(vbus_irq, udc); return 0; } @@ -3201,7 +3202,7 @@ static int nbu2ss_drv_resume(struct platform_device *pdev) if (!udc) return 0; - data = gpio_get_value(VBUS_VALUE); + data = gpiod_get_value(vbus_gpio); if (data) { udc->vbus_active = 1; udc->devstate = USB_STATE_POWERED; diff --git a/drivers/staging/emxx_udc/emxx_udc.h b/drivers/staging/emxx_udc/emxx_udc.h @@ -30,6 +30,8 @@ /* below hacked up for staging integration */ #define GPIO_VBUS 0 /* GPIO_P153 on KZM9D */ #define INT_VBUS 0 /* IRQ for GPIO_P153 */ +struct gpio_desc *vbus_gpio; +int vbus_irq; /*------------ Board dependence(Wait) */ diff --git a/drivers/staging/erofs/Documentation/filesystems/erofs.txt b/drivers/staging/erofs/Documentation/filesystems/erofs.txt @@ -0,0 +1,208 @@ +Overview +======== + +EROFS file-system stands for Enhanced Read-Only File System. Different +from other read-only file systems, it aims to be designed for flexibility, +scalability, but be kept simple and high performance. + +It is designed as a better filesystem solution for the following scenarios: + - read-only storage media or + + - part of a fully trusted read-only solution, which means it needs to be + immutable and bit-for-bit identical to the official golden image for + their releases due to security and other considerations and + + - hope to save some extra storage space with guaranteed end-to-end performance + by using reduced metadata and transparent file compression, especially + for those embedded devices with limited memory (ex, smartphone); + +Here is the main features of EROFS: + - Little endian on-disk design; + + - Currently 4KB block size (nobh) and therefore maximum 16TB address space; + + - Metadata & data could be mixed by design; + + - 2 inode versions for different requirements: + v1 v2 + Inode metadata size: 32 bytes 64 bytes + Max file size: 4 GB 16 EB (also limited by max. vol size) + Max uids/gids: 65536 4294967296 + File creation time: no yes (64 + 32-bit timestamp) + Max hardlinks: 65536 4294967296 + Metadata reserved: 4 bytes 14 bytes + + - Support extended attributes (xattrs) as an option; + + - Support xattr inline and tail-end data inline for all files; + + - Support POSIX.1e ACLs by using xattrs; + + - Support transparent file compression as an option: + LZ4 algorithm with 4 KB fixed-output compression for high performance; + +The following git tree provides the file system user-space tools under +development (ex, formatting tool mkfs.erofs): +>> git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git + +Bugs and patches are welcome, please kindly help us and send to the following +linux-erofs mailing list: +>> linux-erofs mailing list <linux-erofs@lists.ozlabs.org> + +Note that EROFS is still working in progress as a Linux staging driver, +Cc the staging mailing list as well is highly recommended: +>> Linux Driver Project Developer List <devel@driverdev.osuosl.org> + +Mount options +============= + +fault_injection=%d Enable fault injection in all supported types with + specified injection rate. Supported injection type: + Type_Name Type_Value + FAULT_KMALLOC 0x000000001 +(no)user_xattr Setup Extended User Attributes. Note: xattr is enabled + by default if CONFIG_EROFS_FS_XATTR is selected. +(no)acl Setup POSIX Access Control List. Note: acl is enabled + by default if CONFIG_EROFS_FS_POSIX_ACL is selected. + +On-disk details +=============== + +Summary +------- +Different from other read-only file systems, an EROFS volume is designed +to be as simple as possible: + + |-> aligned with the block size + ____________________________________________________________ + | |SB| | ... | Metadata | ... | Data | Metadata | ... | Data | + |_|__|_|_____|__________|_____|______|__________|_____|______| + 0 +1K + +All data areas should be aligned with the block size, but metadata areas +may not. All metadatas can be now observed in two different spaces (views): + 1. Inode metadata space + Each valid inode should be aligned with an inode slot, which is a fixed + value (32 bytes) and designed to be kept in line with v1 inode size. + + Each inode can be directly found with the following formula: + inode offset = meta_blkaddr * block_size + 32 * nid + + |-> aligned with 8B + |-> followed closely + + meta_blkaddr blocks |-> another slot + _____________________________________________________________________ + | ... | inode | xattrs | extents | data inline | ... | inode ... + |________|_______|(optional)|(optional)|__(optional)_|_____|__________ + |-> aligned with the inode slot size + . . + . . + . . + . . + . . + . . + .____________________________________________________|-> aligned with 4B + | xattr_ibody_header | shared xattrs | inline xattrs | + |____________________|_______________|_______________| + |-> 12 bytes <-|->x * 4 bytes<-| . + . . . + . . . + . . . + ._______________________________.______________________. + | id | id | id | id | ... | id | ent | ... | ent| ... | + |____|____|____|____|______|____|_____|_____|____|_____| + |-> aligned with 4B + |-> aligned with 4B + + Inode could be 32 or 64 bytes, which can be distinguished from a common + field which all inode versions have -- i_advise: + + __________________ __________________ + | i_advise | | i_advise | + |__________________| |__________________| + | ... | | ... | + | | | | + |__________________| 32 bytes | | + | | + |__________________| 64 bytes + + Xattrs, extents, data inline are followed by the corresponding inode with + proper alignes, and they could be optional for different data mappings, + _currently_ there are totally 3 valid data mappings supported: + + 1) flat file data without data inline (no extent); + 2) fixed-output size data compression (must have extents); + 3) flat file data with tail-end data inline (no extent); + + The size of the optional xattrs is indicated by i_xattr_count in inode + header. Large xattrs or xattrs shared by many different files can be + stored in shared xattrs metadata rather than inlined right after inode. + + 2. Shared xattrs metadata space + Shared xattrs space is similar to the above inode space, started with + a specific block indicated by xattr_blkaddr, organized one by one with + proper align. + + Each share xattr can also be directly found by the following formula: + xattr offset = xattr_blkaddr * block_size + 4 * xattr_id + + |-> aligned by 4 bytes + + xattr_blkaddr blocks |-> aligned with 4 bytes + _________________________________________________________________________ + | ... | xattr_entry | xattr data | ... | xattr_entry | xattr data ... + |________|_____________|_____________|_____|______________|_______________ + +Directories +----------- +All directories are now organized in a compact on-disk format. Note that +each directory block is divided into index and name areas in order to support +random file lookup, and all directory entries are _strictly_ recorded in +alphabetical order in order to support improved prefix binary search +algorithm (could refer to the related source code). + + ___________________________ + / | + / ______________|________________ + / / | nameoff1 | nameoffN-1 + ____________.______________._______________v________________v__________ +| dirent | dirent | ... | dirent | filename | filename | ... | filename | +|___.0___|____1___|_____|___N-1__|____0_____|____1_____|_____|___N-1____| + \ ^ + \ | * could have + \ | trailing '\0' + \________________________| nameoff0 + + Directory block + +Note that apart from the offset of the first filename, nameoff0 also indicates +the total number of directory entries in this block since it is no need to +introduce another on-disk field at all. + +Compression +----------- +Currently, EROFS supports 4KB fixed-output clustersize transparent file +compression, as illustrated below: + + |---- Variant-Length Extent ----|-------- VLE --------|----- VLE ----- + clusterofs clusterofs clusterofs + | | | logical data +_________v_______________________________v_____________________v_______________ +... | . | | . | | . | ... +____|____.________|_____________|________.____|_____________|__.__________|____ + |-> cluster <-|-> cluster <-|-> cluster <-|-> cluster <-|-> cluster <-| + size size size size size + . . . . + . . . . + . . . . + _______._____________._____________._____________._____________________ + ... | | | | ... physical data + _______|_____________|_____________|_____________|_____________________ + |-> cluster <-|-> cluster <-|-> cluster <-| + size size size + +Currently each on-disk physical cluster can contain 4KB (un)compressed data +at most. For each logical cluster, there is a corresponding on-disk index to +describe its cluster type, physical cluster address, etc. + +See "struct z_erofs_vle_decompressed_index" in erofs_fs.h for more details. + diff --git a/drivers/staging/erofs/Makefile b/drivers/staging/erofs/Makefile @@ -6,7 +6,7 @@ ccflags-y += -Wall -DEROFS_VERSION=\"$(EROFS_VERSION)\" obj-$(CONFIG_EROFS_FS) += erofs.o # staging requirement: to be self-contained in its own directory -ccflags-y += -I$(src)/include +ccflags-y += -I $(srctree)/$(src)/include erofs-objs := super.o inode.o data.o namei.o dir.o utils.o erofs-$(CONFIG_EROFS_FS_XATTR) += xattr.o erofs-$(CONFIG_EROFS_FS_ZIP) += unzip_vle.o unzip_vle_lz4.o diff --git a/drivers/staging/erofs/data.c b/drivers/staging/erofs/data.c @@ -165,43 +165,16 @@ err_out: return err; } -#ifdef CONFIG_EROFS_FS_ZIP -extern int z_erofs_map_blocks_iter(struct inode *, - struct erofs_map_blocks *, - struct page **, int); -#endif - -int erofs_map_blocks_iter(struct inode *inode, - struct erofs_map_blocks *map, - struct page **mpage_ret, int flags) -{ - /* by default, reading raw data never use erofs_map_blocks_iter */ - if (unlikely(!is_inode_layout_compression(inode))) { - if (*mpage_ret) - put_page(*mpage_ret); - *mpage_ret = NULL; - - return erofs_map_blocks(inode, map, flags); - } - -#ifdef CONFIG_EROFS_FS_ZIP - return z_erofs_map_blocks_iter(inode, map, mpage_ret, flags); -#else - /* data compression is not available */ - return -ENOTSUPP; -#endif -} - int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map, int flags) { if (unlikely(is_inode_layout_compression(inode))) { - struct page *mpage = NULL; - int err; + int err = z_erofs_map_blocks_iter(inode, map, flags); - err = erofs_map_blocks_iter(inode, map, &mpage, flags); - if (mpage) - put_page(mpage); + if (map->mpage) { + put_page(map->mpage); + map->mpage = NULL; + } return err; } return erofs_map_blocks_flatmode(inode, map, flags); diff --git a/drivers/staging/erofs/dir.c b/drivers/staging/erofs/dir.c @@ -24,8 +24,8 @@ static const unsigned char erofs_filetype_table[EROFS_FT_MAX] = { }; static int erofs_fill_dentries(struct dir_context *ctx, - void *dentry_blk, unsigned int *ofs, - unsigned int nameoff, unsigned int maxsize) + void *dentry_blk, unsigned int *ofs, + unsigned int nameoff, unsigned int maxsize) { struct erofs_dirent *de = dentry_blk; const struct erofs_dirent *end = dentry_blk + nameoff; @@ -98,15 +98,14 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx) if (IS_ERR(dentry_page)) continue; - lock_page(dentry_page); de = (struct erofs_dirent *)kmap(dentry_page); nameoff = le16_to_cpu(de->nameoff); if (unlikely(nameoff < sizeof(struct erofs_dirent) || - nameoff >= PAGE_SIZE)) { + nameoff >= PAGE_SIZE)) { errln("%s, invalid de[0].nameoff %u", - __func__, nameoff); + __func__, nameoff); err = -EIO; goto skip_this; @@ -128,7 +127,6 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx) skip_this: kunmap(dentry_page); - unlock_page(dentry_page); put_page(dentry_page); ctx->pos = blknr_to_addr(i) + ofs; @@ -144,6 +142,6 @@ skip_this: const struct file_operations erofs_dir_fops = { .llseek = generic_file_llseek, .read = generic_read_dir, - .iterate = erofs_readdir, + .iterate_shared = erofs_readdir, }; diff --git a/drivers/staging/erofs/inode.c b/drivers/staging/erofs/inode.c @@ -184,32 +184,18 @@ static int fill_inode(struct inode *inode, int isdir) if (!err) { /* setup the new inode */ if (S_ISREG(inode->i_mode)) { -#ifdef CONFIG_EROFS_FS_XATTR - if (vi->xattr_isize) - inode->i_op = &erofs_generic_xattr_iops; -#endif + inode->i_op = &erofs_generic_iops; inode->i_fop = &generic_ro_fops; } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = -#ifdef CONFIG_EROFS_FS_XATTR - vi->xattr_isize ? &erofs_dir_xattr_iops : -#endif - &erofs_dir_iops; + inode->i_op = &erofs_dir_iops; inode->i_fop = &erofs_dir_fops; } else if (S_ISLNK(inode->i_mode)) { /* by default, page_get_link is used for symlink */ - inode->i_op = -#ifdef CONFIG_EROFS_FS_XATTR - &erofs_symlink_xattr_iops, -#else - &page_symlink_inode_operations; -#endif + inode->i_op = &erofs_symlink_iops; inode_nohighmem(inode); } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { -#ifdef CONFIG_EROFS_FS_XATTR - inode->i_op = &erofs_special_inode_operations; -#endif + inode->i_op = &erofs_generic_iops; init_special_inode(inode, inode->i_mode, inode->i_rdev); } else { err = -EIO; @@ -297,23 +283,26 @@ struct inode *erofs_iget(struct super_block *sb, return inode; } +const struct inode_operations erofs_generic_iops = { #ifdef CONFIG_EROFS_FS_XATTR -const struct inode_operations erofs_generic_xattr_iops = { .listxattr = erofs_listxattr, +#endif + .get_acl = erofs_get_acl, }; -const struct inode_operations erofs_symlink_xattr_iops = { +const struct inode_operations erofs_symlink_iops = { .get_link = page_get_link, +#ifdef CONFIG_EROFS_FS_XATTR .listxattr = erofs_listxattr, +#endif + .get_acl = erofs_get_acl, }; -const struct inode_operations erofs_special_inode_operations = { - .listxattr = erofs_listxattr, -}; - -const struct inode_operations erofs_fast_symlink_xattr_iops = { +const struct inode_operations erofs_fast_symlink_iops = { .get_link = simple_get_link, +#ifdef CONFIG_EROFS_FS_XATTR .listxattr = erofs_listxattr, -}; #endif + .get_acl = erofs_get_acl, +}; diff --git a/drivers/staging/erofs/internal.h b/drivers/staging/erofs/internal.h @@ -252,47 +252,20 @@ static inline int erofs_wait_on_workgroup_freezed(struct erofs_workgroup *grp) } #endif -static inline bool erofs_workgroup_get(struct erofs_workgroup *grp, int *ocnt) -{ - int o; - -repeat: - o = erofs_wait_on_workgroup_freezed(grp); - - if (unlikely(o <= 0)) - return -1; - - if (unlikely(atomic_cmpxchg(&grp->refcount, o, o + 1) != o)) - goto repeat; - - *ocnt = o; - return 0; -} - -#define __erofs_workgroup_get(grp) atomic_inc(&(grp)->refcount) -#define __erofs_workgroup_put(grp) atomic_dec(&(grp)->refcount) - -extern int erofs_workgroup_put(struct erofs_workgroup *grp); - -extern struct erofs_workgroup *erofs_find_workgroup( - struct super_block *sb, pgoff_t index, bool *tag); - -extern int erofs_register_workgroup(struct super_block *sb, - struct erofs_workgroup *grp, bool tag); - -extern unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi, - unsigned long nr_shrink, bool cleanup); - -static inline void erofs_workstation_cleanup_all(struct super_block *sb) -{ - erofs_shrink_workstation(EROFS_SB(sb), ~0UL, true); -} +int erofs_workgroup_put(struct erofs_workgroup *grp); +struct erofs_workgroup *erofs_find_workgroup(struct super_block *sb, + pgoff_t index, bool *tag); +int erofs_register_workgroup(struct super_block *sb, + struct erofs_workgroup *grp, bool tag); +unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi, + unsigned long nr_shrink, bool cleanup); +void erofs_workgroup_free_rcu(struct erofs_workgroup *grp); #ifdef EROFS_FS_HAS_MANAGED_CACHE -extern int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi, - struct erofs_workgroup *egrp); -extern int erofs_try_to_free_cached_page(struct address_space *mapping, - struct page *page); +int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi, + struct erofs_workgroup *egrp); +int erofs_try_to_free_cached_page(struct address_space *mapping, + struct page *page); #define MNGD_MAPPING(sbi) ((sbi)->managed_cache->i_mapping) #else @@ -354,12 +327,17 @@ static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid) return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits); } -#define inode_set_inited_xattr(inode) (EROFS_V(inode)->flags |= 1) -#define inode_has_inited_xattr(inode) (EROFS_V(inode)->flags & 1) +/* atomic flag definitions */ +#define EROFS_V_EA_INITED_BIT 0 + +/* bitlock definitions (arranged in reverse order) */ +#define EROFS_V_BL_XATTR_BIT (BITS_PER_LONG - 1) struct erofs_vnode { erofs_nid_t nid; - unsigned int flags; + + /* atomic flags (including bitlocks) */ + unsigned long flags; unsigned char data_mapping_mode; /* inline size in bytes */ @@ -412,8 +390,6 @@ static inline bool is_inode_layout_inline(struct inode *inode) } extern const struct super_operations erofs_sops; -extern const struct inode_operations erofs_dir_iops; -extern const struct file_operations erofs_dir_fops; extern const struct address_space_operations erofs_raw_access_aops; #ifdef CONFIG_EROFS_FS_ZIP @@ -461,11 +437,26 @@ struct erofs_map_blocks { u64 m_plen, m_llen; unsigned int m_flags; + + struct page *mpage; }; /* Flags used by erofs_map_blocks() */ #define EROFS_GET_BLOCKS_RAW 0x0001 +#ifdef CONFIG_EROFS_FS_ZIP +int z_erofs_map_blocks_iter(struct inode *inode, + struct erofs_map_blocks *map, + int flags); +#else +static inline int z_erofs_map_blocks_iter(struct inode *inode, + struct erofs_map_blocks *map, + int flags) +{ + return -ENOTSUPP; +} +#endif + /* data.c */ static inline struct bio * erofs_grab_bio(struct super_block *sb, @@ -506,8 +497,8 @@ static inline void __submit_bio(struct bio *bio, unsigned op, unsigned op_flags) #define EROFS_IO_MAX_RETRIES_NOFAIL CONFIG_EROFS_FS_IO_MAX_RETRIES #endif -extern struct page *__erofs_get_meta_page(struct super_block *sb, - erofs_blk_t blkaddr, bool prio, bool nofail); +struct page *__erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr, + bool prio, bool nofail); static inline struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr, bool prio) @@ -521,15 +512,7 @@ static inline struct page *erofs_get_meta_page_nofail(struct super_block *sb, return __erofs_get_meta_page(sb, blkaddr, prio, true); } -extern int erofs_map_blocks(struct inode *, struct erofs_map_blocks *, int); -extern int erofs_map_blocks_iter(struct inode *, struct erofs_map_blocks *, - struct page **, int); - -struct erofs_map_blocks_iter { - struct erofs_map_blocks map; - struct page *mpage; -}; - +int erofs_map_blocks(struct inode *, struct erofs_map_blocks *, int); static inline struct page * erofs_get_inline_page(struct inode *inode, @@ -549,41 +532,31 @@ static inline unsigned long erofs_inode_hash(erofs_nid_t nid) #endif } -extern struct inode *erofs_iget(struct super_block *sb, - erofs_nid_t nid, bool dir); - -/* dir.c */ -int erofs_namei(struct inode *dir, struct qstr *name, - erofs_nid_t *nid, unsigned *d_type); - -#ifdef CONFIG_EROFS_FS_XATTR -/* xattr.c */ -extern const struct xattr_handler *erofs_xattr_handlers[]; - -/* symlink and special inode */ -extern const struct inode_operations erofs_symlink_xattr_iops; -extern const struct inode_operations erofs_fast_symlink_xattr_iops; -extern const struct inode_operations erofs_special_inode_operations; -#endif +extern const struct inode_operations erofs_generic_iops; +extern const struct inode_operations erofs_symlink_iops; +extern const struct inode_operations erofs_fast_symlink_iops; static inline void set_inode_fast_symlink(struct inode *inode) { -#ifdef CONFIG_EROFS_FS_XATTR - inode->i_op = &erofs_fast_symlink_xattr_iops; -#else - inode->i_op = &simple_symlink_inode_operations; -#endif + inode->i_op = &erofs_fast_symlink_iops; } static inline bool is_inode_fast_symlink(struct inode *inode) { -#ifdef CONFIG_EROFS_FS_XATTR - return inode->i_op == &erofs_fast_symlink_xattr_iops; -#else - return inode->i_op == &simple_symlink_inode_operations; -#endif + return inode->i_op == &erofs_fast_symlink_iops; } +struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid, bool dir); + +/* namei.c */ +extern const struct inode_operations erofs_dir_iops; + +int erofs_namei(struct inode *dir, struct qstr *name, + erofs_nid_t *nid, unsigned int *d_type); + +/* dir.c */ +extern const struct file_operations erofs_dir_fops; + static inline void *erofs_vmap(struct page **pages, unsigned int count) { #ifdef CONFIG_EROFS_FS_USE_VM_MAP_RAM @@ -612,15 +585,11 @@ static inline void erofs_vunmap(const void *mem, unsigned int count) } /* utils.c */ -extern struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp); - -extern void erofs_register_super(struct super_block *sb); -extern void erofs_unregister_super(struct super_block *sb); +extern struct shrinker erofs_shrinker_info; -extern unsigned long erofs_shrink_count(struct shrinker *shrink, - struct shrink_control *sc); -extern unsigned long erofs_shrink_scan(struct shrinker *shrink, - struct shrink_control *sc); +struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp); +void erofs_register_super(struct super_block *sb); +void erofs_unregister_super(struct super_block *sb); #ifndef lru_to_page #define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) diff --git a/drivers/staging/erofs/namei.c b/drivers/staging/erofs/namei.c @@ -15,74 +15,77 @@ #include <trace/events/erofs.h> -/* based on the value of qn->len is accurate */ -static inline int dirnamecmp(struct qstr *qn, - struct qstr *qd, unsigned int *matched) +struct erofs_qstr { + const unsigned char *name; + const unsigned char *end; +}; + +/* based on the end of qn is accurate and it must have the trailing '\0' */ +static inline int dirnamecmp(const struct erofs_qstr *qn, + const struct erofs_qstr *qd, + unsigned int *matched) { - unsigned int i = *matched, len = min(qn->len, qd->len); -loop: - if (unlikely(i >= len)) { - *matched = i; - if (qn->len < qd->len) { - /* - * actually (qn->len == qd->len) - * when qd->name[i] == '\0' - */ - return qd->name[i] == '\0' ? 0 : -1; + unsigned int i = *matched; + + /* + * on-disk error, let's only BUG_ON in the debugging mode. + * otherwise, it will return 1 to just skip the invalid name + * and go on (in consideration of the lookup performance). + */ + DBG_BUGON(qd->name > qd->end); + + /* qd could not have trailing '\0' */ + /* However it is absolutely safe if < qd->end */ + while (qd->name + i < qd->end && qd->name[i] != '\0') { + if (qn->name[i] != qd->name[i]) { + *matched = i; + return qn->name[i] > qd->name[i] ? 1 : -1; } - return (qn->len > qd->len); - } - - if (qn->name[i] != qd->name[i]) { - *matched = i; - return qn->name[i] > qd->name[i] ? 1 : -1; + ++i; } - - ++i; - goto loop; + *matched = i; + /* See comments in __d_alloc on the terminating NUL character */ + return qn->name[i] == '\0' ? 0 : 1; } -static struct erofs_dirent *find_target_dirent( - struct qstr *name, - u8 *data, int maxsize) +#define nameoff_from_disk(off, sz) (le16_to_cpu(off) & ((sz) - 1)) + +static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name, + u8 *data, + unsigned int dirblksize, + const int ndirents) { - unsigned int ndirents, head, back; + int head, back; unsigned int startprfx, endprfx; struct erofs_dirent *const de = (struct erofs_dirent *)data; - /* make sure that maxsize is valid */ - BUG_ON(maxsize < sizeof(struct erofs_dirent)); - - ndirents = le16_to_cpu(de->nameoff) / sizeof(*de); - - /* corrupted dir (may be unnecessary...) */ - BUG_ON(!ndirents); - - head = 0; + /* since the 1st dirent has been evaluated previously */ + head = 1; back = ndirents - 1; startprfx = endprfx = 0; while (head <= back) { - unsigned int mid = head + (back - head) / 2; - unsigned int nameoff = le16_to_cpu(de[mid].nameoff); + const int mid = head + (back - head) / 2; + const int nameoff = nameoff_from_disk(de[mid].nameoff, + dirblksize); unsigned int matched = min(startprfx, endprfx); - - struct qstr dname = QSTR_INIT(data + nameoff, - unlikely(mid >= ndirents - 1) ? - maxsize - nameoff : - le16_to_cpu(de[mid + 1].nameoff) - nameoff); + struct erofs_qstr dname = { + .name = data + nameoff, + .end = unlikely(mid >= ndirents - 1) ? + data + dirblksize : + data + nameoff_from_disk(de[mid + 1].nameoff, + dirblksize) + }; /* string comparison without already matched prefix */ int ret = dirnamecmp(name, &dname, &matched); - if (unlikely(!ret)) + if (unlikely(!ret)) { return de + mid; - else if (ret > 0) { + } else if (ret > 0) { head = mid + 1; startprfx = matched; - } else if (unlikely(mid < 1)) /* fix "mid" overflow */ - break; - else { + } else { back = mid - 1; endprfx = matched; } @@ -91,12 +94,12 @@ static struct erofs_dirent *find_target_dirent( return ERR_PTR(-ENOENT); } -static struct page *find_target_block_classic( - struct inode *dir, - struct qstr *name, int *_diff) +static struct page *find_target_block_classic(struct inode *dir, + struct erofs_qstr *name, + int *_ndirents) { unsigned int startprfx, endprfx; - unsigned int head, back; + int head, back; struct address_space *const mapping = dir->i_mapping; struct page *candidate = ERR_PTR(-ENOENT); @@ -105,89 +108,97 @@ static struct page *find_target_block_classic( back = inode_datablocks(dir) - 1; while (head <= back) { - unsigned int mid = head + (back - head) / 2; + const int mid = head + (back - head) / 2; struct page *page = read_mapping_page(mapping, mid, NULL); - if (IS_ERR(page)) { -exact_out: - if (!IS_ERR(candidate)) /* valid candidate */ - put_page(candidate); - return page; - } else { - int diff; - unsigned int ndirents, matched; - struct qstr dname; + if (!IS_ERR(page)) { struct erofs_dirent *de = kmap_atomic(page); - unsigned int nameoff = le16_to_cpu(de->nameoff); - - ndirents = nameoff / sizeof(*de); + const int nameoff = nameoff_from_disk(de->nameoff, + EROFS_BLKSIZ); + const int ndirents = nameoff / sizeof(*de); + int diff; + unsigned int matched; + struct erofs_qstr dname; - /* corrupted dir (should have one entry at least) */ - BUG_ON(!ndirents || nameoff > PAGE_SIZE); + if (unlikely(!ndirents)) { + DBG_BUGON(1); + kunmap_atomic(de); + put_page(page); + page = ERR_PTR(-EIO); + goto out; + } matched = min(startprfx, endprfx); dname.name = (u8 *)de + nameoff; - dname.len = ndirents == 1 ? - /* since the rest of the last page is 0 */ - EROFS_BLKSIZ - nameoff - : le16_to_cpu(de[1].nameoff) - nameoff; + if (ndirents == 1) + dname.end = (u8 *)de + EROFS_BLKSIZ; + else + dname.end = (u8 *)de + + nameoff_from_disk(de[1].nameoff, + EROFS_BLKSIZ); /* string comparison without already matched prefix */ diff = dirnamecmp(name, &dname, &matched); kunmap_atomic(de); if (unlikely(!diff)) { - *_diff = 0; - goto exact_out; + *_ndirents = 0; + goto out; } else if (diff > 0) { head = mid + 1; startprfx = matched; - if (likely(!IS_ERR(candidate))) + if (!IS_ERR(candidate)) put_page(candidate); candidate = page; + *_ndirents = ndirents; } else { put_page(page); - if (unlikely(mid < 1)) /* fix "mid" overflow */ - break; - back = mid - 1; endprfx = matched; } + continue; } +out: /* free if the candidate is valid */ + if (!IS_ERR(candidate)) + put_page(candidate); + return page; } - *_diff = 1; return candidate; } int erofs_namei(struct inode *dir, - struct qstr *name, - erofs_nid_t *nid, unsigned int *d_type) + struct qstr *name, + erofs_nid_t *nid, unsigned int *d_type) { - int diff; + int ndirents; struct page *page; - u8 *data; + void *data; struct erofs_dirent *de; + struct erofs_qstr qn; if (unlikely(!dir->i_size)) return -ENOENT; - diff = 1; - page = find_target_block_classic(dir, name, &diff); + qn.name = name->name; + qn.end = name->name + name->len; + + ndirents = 0; + page = find_target_block_classic(dir, &qn, &ndirents); - if (unlikely(IS_ERR(page))) + if (IS_ERR(page)) return PTR_ERR(page); data = kmap_atomic(page); /* the target page has been mapped */ - de = likely(diff) ? - /* since the rest of the last page is 0 */ - find_target_dirent(name, data, EROFS_BLKSIZ) : - (struct erofs_dirent *)data; + if (ndirents) + de = find_target_dirent(&qn, data, EROFS_BLKSIZ, ndirents); + else + de = (struct erofs_dirent *)data; - if (likely(!IS_ERR(de))) { + if (!IS_ERR(de)) { *nid = le64_to_cpu(de->nid); *d_type = de->file_type; } @@ -235,12 +246,9 @@ static struct dentry *erofs_lookup(struct inode *dir, const struct inode_operations erofs_dir_iops = { .lookup = erofs_lookup, -}; - -const struct inode_operations erofs_dir_xattr_iops = { - .lookup = erofs_lookup, #ifdef CONFIG_EROFS_FS_XATTR .listxattr = erofs_listxattr, #endif + .get_acl = erofs_get_acl, }; diff --git a/drivers/staging/erofs/super.c b/drivers/staging/erofs/super.c @@ -16,6 +16,7 @@ #include <linux/parser.h> #include <linux/seq_file.h> #include "internal.h" +#include "xattr.h" #define CREATE_TRACE_POINTS #include <trace/events/erofs.h> @@ -397,6 +398,11 @@ static int erofs_read_super(struct super_block *sb, if (!silent) infoln("root inode @ nid %llu", ROOT_NID(sbi)); + if (test_opt(sbi, POSIX_ACL)) + sb->s_flags |= SB_POSIXACL; + else + sb->s_flags &= ~SB_POSIXACL; + #ifdef CONFIG_EROFS_FS_ZIP INIT_RADIX_TREE(&sbi->workstn_tree, GFP_ATOMIC); #endif @@ -420,13 +426,14 @@ static int erofs_read_super(struct super_block *sb, errln("rootino(nid %llu) is not a directory(i_mode %o)", ROOT_NID(sbi), inode->i_mode); err = -EINVAL; - goto err_isdir; + iput(inode); + goto err_iget; } sb->s_root = d_make_root(inode); if (sb->s_root == NULL) { err = -ENOMEM; - goto err_makeroot; + goto err_iget; } /* save the device name to sbi */ @@ -452,10 +459,6 @@ static int erofs_read_super(struct super_block *sb, */ err_devname: dput(sb->s_root); -err_makeroot: -err_isdir: - if (sb->s_root == NULL) - iput(inode); err_iget: #ifdef EROFS_FS_HAS_MANAGED_CACHE iput(sbi->managed_cache); @@ -493,7 +496,8 @@ static void erofs_put_super(struct super_block *sb) mutex_lock(&sbi->umount_mutex); #ifdef CONFIG_EROFS_FS_ZIP - erofs_workstation_cleanup_all(sb); + /* clean up the compression space of this sb */ + erofs_shrink_workstation(EROFS_SB(sb), ~0UL, true); #endif erofs_unregister_super(sb); @@ -537,12 +541,6 @@ static void erofs_kill_sb(struct super_block *sb) kill_block_super(sb); } -static struct shrinker erofs_shrinker_info = { - .scan_objects = erofs_shrink_scan, - .count_objects = erofs_shrink_count, - .seeks = DEFAULT_SEEKS, -}; - static struct file_system_type erofs_fs_type = { .owner = THIS_MODULE, .name = "erofs", @@ -653,6 +651,11 @@ static int erofs_remount(struct super_block *sb, int *flags, char *data) if (err) goto out; + if (test_opt(sbi, POSIX_ACL)) + sb->s_flags |= SB_POSIXACL; + else + sb->s_flags &= ~SB_POSIXACL; + *flags |= SB_RDONLY; return 0; out: diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c @@ -107,15 +107,30 @@ enum z_erofs_vle_work_role { Z_EROFS_VLE_WORK_SECONDARY, Z_EROFS_VLE_WORK_PRIMARY, /* - * The current work has at least been linked with the following - * processed chained works, which means if the processing page - * is the tail partial page of the work, the current work can - * safely use the whole page, as illustrated below: - * +--------------+-------------------------------------------+ - * | tail page | head page (of the previous work) | - * +--------------+-------------------------------------------+ - * /\ which belongs to the current work - * [ (*) this page can be used for the current work itself. ] + * The current work was the tail of an exist chain, and the previous + * processed chained works are all decided to be hooked up to it. + * A new chain should be created for the remaining unprocessed works, + * therefore different from Z_EROFS_VLE_WORK_PRIMARY_FOLLOWED, + * the next work cannot reuse the whole page in the following scenario: + * ________________________________________________________________ + * | tail (partial) page | head (partial) page | + * | (belongs to the next work) | (belongs to the current work) | + * |_______PRIMARY_FOLLOWED_______|________PRIMARY_HOOKED___________| + */ + Z_EROFS_VLE_WORK_PRIMARY_HOOKED, + /* + * The current work has been linked with the processed chained works, + * and could be also linked with the potential remaining works, which + * means if the processing page is the tail partial page of the work, + * the current work can safely use the whole page (since the next work + * is under control) for in-place decompression, as illustrated below: + * ________________________________________________________________ + * | tail (partial) page | head (partial) page | + * | (of the current work) | (of the previous work) | + * | PRIMARY_FOLLOWED or | | + * |_____PRIMARY_HOOKED____|____________PRIMARY_FOLLOWED____________| + * + * [ (*) the above page can be used for the current work itself. ] */ Z_EROFS_VLE_WORK_PRIMARY_FOLLOWED, Z_EROFS_VLE_WORK_MAX @@ -238,14 +253,9 @@ int erofs_try_to_free_cached_page(struct address_space *mapping, { struct erofs_sb_info *const sbi = EROFS_SB(mapping->host->i_sb); const unsigned int clusterpages = erofs_clusterpages(sbi); - - struct z_erofs_vle_workgroup *grp; + struct z_erofs_vle_workgroup *const grp = (void *)page_private(page); int ret = 0; /* 0 - busy */ - /* prevent the workgroup from being freed */ - rcu_read_lock(); - grp = (void *)page_private(page); - if (erofs_workgroup_try_to_freeze(&grp->obj, 1)) { unsigned int i; @@ -257,12 +267,11 @@ int erofs_try_to_free_cached_page(struct address_space *mapping, } } erofs_workgroup_unfreeze(&grp->obj, 1); - } - rcu_read_unlock(); - if (ret) { - ClearPagePrivate(page); - put_page(page); + if (ret) { + ClearPagePrivate(page); + put_page(page); + } } return ret; } @@ -315,10 +324,10 @@ static int z_erofs_vle_work_add_page( return ret ? 0 : -EAGAIN; } -static inline bool try_to_claim_workgroup( - struct z_erofs_vle_workgroup *grp, - z_erofs_vle_owned_workgrp_t *owned_head, - bool *hosted) +static enum z_erofs_vle_work_role +try_to_claim_workgroup(struct z_erofs_vle_workgroup *grp, + z_erofs_vle_owned_workgrp_t *owned_head, + bool *hosted) { DBG_BUGON(*hosted == true); @@ -332,6 +341,9 @@ retry: *owned_head = &grp->next; *hosted = true; + /* lucky, I am the followee :) */ + return Z_EROFS_VLE_WORK_PRIMARY_FOLLOWED; + } else if (grp->next == Z_EROFS_VLE_WORKGRP_TAIL) { /* * type 2, link to the end of a existing open chain, @@ -341,12 +353,11 @@ retry: if (cmpxchg(&grp->next, Z_EROFS_VLE_WORKGRP_TAIL, *owned_head) != Z_EROFS_VLE_WORKGRP_TAIL) goto retry; - *owned_head = Z_EROFS_VLE_WORKGRP_TAIL; - } else - return false; /* :( better luck next time */ + return Z_EROFS_VLE_WORK_PRIMARY_HOOKED; + } - return true; /* lucky, I am the followee :) */ + return Z_EROFS_VLE_WORK_PRIMARY; /* :( better luck next time */ } struct z_erofs_vle_work_finder { @@ -424,12 +435,9 @@ z_erofs_vle_work_lookup(const struct z_erofs_vle_work_finder *f) *f->hosted = false; if (!primary) *f->role = Z_EROFS_VLE_WORK_SECONDARY; - /* claim the workgroup if possible */ - else if (try_to_claim_workgroup(grp, f->owned_head, f->hosted)) - *f->role = Z_EROFS_VLE_WORK_PRIMARY_FOLLOWED; - else - *f->role = Z_EROFS_VLE_WORK_PRIMARY; - + else /* claim the workgroup if possible */ + *f->role = try_to_claim_workgroup(grp, f->owned_head, + f->hosted); return work; } @@ -493,6 +501,9 @@ z_erofs_vle_work_register(const struct z_erofs_vle_work_finder *f, return work; } +#define builder_is_hooked(builder) \ + ((builder)->role >= Z_EROFS_VLE_WORK_PRIMARY_HOOKED) + #define builder_is_followed(builder) \ ((builder)->role >= Z_EROFS_VLE_WORK_PRIMARY_FOLLOWED) @@ -539,7 +550,7 @@ repeat: if (unlikely(work == ERR_PTR(-EAGAIN))) goto repeat; - if (unlikely(IS_ERR(work))) + if (IS_ERR(work)) return PTR_ERR(work); got_it: z_erofs_pagevec_ctor_init(&builder->vector, @@ -589,7 +600,7 @@ static void __z_erofs_vle_work_release(struct z_erofs_vle_workgroup *grp, erofs_workgroup_put(&grp->obj); } -void z_erofs_vle_work_release(struct z_erofs_vle_work *work) +static void z_erofs_vle_work_release(struct z_erofs_vle_work *work) { struct z_erofs_vle_workgroup *grp = z_erofs_vle_work_workgroup(work, true); @@ -636,7 +647,7 @@ struct z_erofs_vle_frontend { struct inode *const inode; struct z_erofs_vle_work_builder builder; - struct erofs_map_blocks_iter m_iter; + struct erofs_map_blocks map; z_erofs_vle_owned_workgrp_t owned_head; @@ -647,8 +658,9 @@ struct z_erofs_vle_frontend { #define VLE_FRONTEND_INIT(__i) { \ .inode = __i, \ - .m_iter = { \ - { .m_llen = 0, .m_plen = 0 }, \ + .map = { \ + .m_llen = 0, \ + .m_plen = 0, \ .mpage = NULL \ }, \ .builder = VLE_WORK_BUILDER_INIT(), \ @@ -681,12 +693,11 @@ static int z_erofs_do_read_page(struct z_erofs_vle_frontend *fe, { struct super_block *const sb = fe->inode->i_sb; struct erofs_sb_info *const sbi __maybe_unused = EROFS_SB(sb); - struct erofs_map_blocks_iter *const m = &fe->m_iter; - struct erofs_map_blocks *const map = &m->map; + struct erofs_map_blocks *const map = &fe->map; struct z_erofs_vle_work_builder *const builder = &fe->builder; const loff_t offset = page_offset(page); - bool tight = builder_is_followed(builder); + bool tight = builder_is_hooked(builder); struct z_erofs_vle_work *work = builder->work; enum z_erofs_cache_alloctype cache_strategy; @@ -704,8 +715,12 @@ repeat: /* lucky, within the range of the current map_blocks */ if (offset + cur >= map->m_la && - offset + cur < map->m_la + map->m_llen) + offset + cur < map->m_la + map->m_llen) { + /* didn't get a valid unzip work previously (very rare) */ + if (!builder->work) + goto restart_now; goto hitted; + } /* go ahead the next map_blocks */ debugln("%s: [out-of-range] pos %llu", __func__, offset + cur); @@ -715,10 +730,11 @@ repeat: map->m_la = offset + cur; map->m_llen = 0; - err = erofs_map_blocks_iter(fe->inode, map, &m->mpage, 0); + err = z_erofs_map_blocks_iter(fe->inode, map, 0); if (unlikely(err)) goto err_out; +restart_now: if (unlikely(!(map->m_flags & EROFS_MAP_MAPPED))) goto hitted; @@ -740,7 +756,7 @@ repeat: map->m_plen / PAGE_SIZE, cache_strategy, page_pool, GFP_KERNEL); - tight &= builder_is_followed(builder); + tight &= builder_is_hooked(builder); work = builder->work; hitted: cur = end - min_t(unsigned int, offset + end - map->m_la, end); @@ -755,6 +771,9 @@ hitted: (tight ? Z_EROFS_PAGE_TYPE_EXCLUSIVE : Z_EROFS_VLE_PAGE_TYPE_TAIL_SHARED)); + if (cur) + tight &= builder_is_followed(builder); + retry: err = z_erofs_vle_work_add_page(builder, page, page_type); /* should allocate an additional staging page for pagevec */ @@ -992,11 +1011,10 @@ repeat: if (llen > grp->llen) llen = grp->llen; - err = z_erofs_vle_unzip_fast_percpu(compressed_pages, - clusterpages, pages, llen, work->pageofs, - z_erofs_onlinepage_endio); + err = z_erofs_vle_unzip_fast_percpu(compressed_pages, clusterpages, + pages, llen, work->pageofs); if (err != -ENOTSUPP) - goto out_percpu; + goto out; if (sparsemem_pages >= nr_pages) goto skip_allocpage; @@ -1017,8 +1035,25 @@ skip_allocpage: erofs_vunmap(vout, nr_pages); out: + /* must handle all compressed pages before endding pages */ + for (i = 0; i < clusterpages; ++i) { + page = compressed_pages[i]; + +#ifdef EROFS_FS_HAS_MANAGED_CACHE + if (page->mapping == MNGD_MAPPING(sbi)) + continue; +#endif + /* recycle all individual staging pages */ + (void)z_erofs_gather_if_stagingpage(page_pool, page); + + WRITE_ONCE(compressed_pages[i], NULL); + } + for (i = 0; i < nr_pages; ++i) { page = pages[i]; + if (!page) + continue; + DBG_BUGON(!page->mapping); /* recycle all individual staging pages */ @@ -1031,20 +1066,6 @@ out: z_erofs_onlinepage_endio(page); } -out_percpu: - for (i = 0; i < clusterpages; ++i) { - page = compressed_pages[i]; - -#ifdef EROFS_FS_HAS_MANAGED_CACHE - if (page->mapping == MNGD_MAPPING(sbi)) - continue; -#endif - /* recycle all individual staging pages */ - (void)z_erofs_gather_if_stagingpage(page_pool, page); - - WRITE_ONCE(compressed_pages[i], NULL); - } - if (pages == z_pagemap_global) mutex_unlock(&z_pagemap_global_lock); else if (unlikely(pages != pages_onstack)) @@ -1484,8 +1505,8 @@ static int z_erofs_vle_normalaccess_readpage(struct file *file, z_erofs_submit_and_unzip(&f, &pagepool, true); out: - if (f.m_iter.mpage) - put_page(f.m_iter.mpage); + if (f.map.mpage) + put_page(f.map.mpage); /* clean up the remaining free pages */ put_pages_list(&pagepool); @@ -1555,8 +1576,8 @@ static int z_erofs_vle_normalaccess_readpages(struct file *filp, z_erofs_submit_and_unzip(&f, &pagepool, sync); - if (f.m_iter.mpage) - put_page(f.m_iter.mpage); + if (f.map.mpage) + put_page(f.map.mpage); /* clean up the remaining free pages */ put_pages_list(&pagepool); @@ -1701,14 +1722,14 @@ vle_get_logical_extent_head(const struct vle_map_blocks_iter_ctx *ctx, int z_erofs_map_blocks_iter(struct inode *inode, struct erofs_map_blocks *map, - struct page **mpage_ret, int flags) + int flags) { void *kaddr; const struct vle_map_blocks_iter_ctx ctx = { .inode = inode, .sb = inode->i_sb, .clusterbits = EROFS_I_SB(inode)->clusterbits, - .mpage_ret = mpage_ret, + .mpage_ret = &map->mpage, .kaddr_ret = &kaddr }; const unsigned int clustersize = 1 << ctx.clusterbits; @@ -1722,7 +1743,7 @@ int z_erofs_map_blocks_iter(struct inode *inode, /* initialize `pblk' to keep gcc from printing foolish warnings */ erofs_blk_t mblk, pblk = 0; - struct page *mpage = *mpage_ret; + struct page *mpage = map->mpage; struct z_erofs_vle_decompressed_index *di; unsigned int cluster_type, logical_cluster_ofs; int err = 0; @@ -1758,7 +1779,7 @@ int z_erofs_map_blocks_iter(struct inode *inode, err = PTR_ERR(mpage); goto out; } - *mpage_ret = mpage; + map->mpage = mpage; } else { lock_page(mpage); DBG_BUGON(!PageUptodate(mpage)); @@ -1818,7 +1839,7 @@ int z_erofs_map_blocks_iter(struct inode *inode, /* get the correspoinding first chunk */ err = vle_get_logical_extent_head(&ctx, lcn, &ofs, &pblk, &map->m_flags); - mpage = *mpage_ret; + mpage = map->mpage; if (unlikely(err)) { if (mpage) diff --git a/drivers/staging/erofs/unzip_vle.h b/drivers/staging/erofs/unzip_vle.h @@ -212,18 +212,17 @@ static inline void z_erofs_onlinepage_endio(struct page *page) #define Z_EROFS_VLE_VMAP_GLOBAL_PAGES 2048 /* unzip_vle_lz4.c */ -extern int z_erofs_vle_plain_copy(struct page **compressed_pages, - unsigned clusterpages, struct page **pages, - unsigned nr_pages, unsigned short pageofs); - -extern int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, - unsigned clusterpages, struct page **pages, - unsigned outlen, unsigned short pageofs, - void (*endio)(struct page *)); - -extern int z_erofs_vle_unzip_vmap(struct page **compressed_pages, - unsigned clusterpages, void *vaddr, unsigned llen, - unsigned short pageofs, bool overlapped); +int z_erofs_vle_plain_copy(struct page **compressed_pages, + unsigned int clusterpages, struct page **pages, + unsigned int nr_pages, unsigned short pageofs); +int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, + unsigned int clusterpages, + struct page **pages, unsigned int outlen, + unsigned short pageofs); +int z_erofs_vle_unzip_vmap(struct page **compressed_pages, + unsigned int clusterpages, + void *vaddr, unsigned int llen, + unsigned short pageofs, bool overlapped); #endif diff --git a/drivers/staging/erofs/unzip_vle_lz4.c b/drivers/staging/erofs/unzip_vle_lz4.c @@ -13,7 +13,7 @@ #include "unzip_vle.h" #include <linux/lz4.h> -int z_erofs_unzip_lz4(void *in, void *out, size_t inlen, size_t outlen) +static int z_erofs_unzip_lz4(void *in, void *out, size_t inlen, size_t outlen) { int ret = LZ4_decompress_safe_partial(in, out, inlen, outlen, outlen); @@ -125,8 +125,7 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, unsigned int clusterpages, struct page **pages, unsigned int outlen, - unsigned short pageofs, - void (*endio)(struct page *)) + unsigned short pageofs) { void *vin, *vout; unsigned int nr_pages, i, j; @@ -148,19 +147,16 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, ret = z_erofs_unzip_lz4(vin, vout + pageofs, clusterpages * PAGE_SIZE, outlen); - if (ret >= 0) { - outlen = ret; - ret = 0; - } + if (ret < 0) + goto out; + ret = 0; for (i = 0; i < nr_pages; ++i) { j = min((unsigned int)PAGE_SIZE - pageofs, outlen); if (pages[i]) { - if (ret < 0) { - SetPageError(pages[i]); - } else if (clusterpages == 1 && - pages[i] == compressed_pages[0]) { + if (clusterpages == 1 && + pages[i] == compressed_pages[0]) { memcpy(vin + pageofs, vout + pageofs, j); } else { void *dst = kmap_atomic(pages[i]); @@ -168,12 +164,13 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, memcpy(dst + pageofs, vout + pageofs, j); kunmap_atomic(dst); } - endio(pages[i]); } vout += PAGE_SIZE; outlen -= j; pageofs = 0; } + +out: preempt_enable(); if (clusterpages == 1) diff --git a/drivers/staging/erofs/utils.c b/drivers/staging/erofs/utils.c @@ -31,13 +31,32 @@ struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp) static atomic_long_t erofs_global_shrink_cnt; #ifdef CONFIG_EROFS_FS_ZIP +#define __erofs_workgroup_get(grp) atomic_inc(&(grp)->refcount) +#define __erofs_workgroup_put(grp) atomic_dec(&(grp)->refcount) -struct erofs_workgroup *erofs_find_workgroup( - struct super_block *sb, pgoff_t index, bool *tag) +static int erofs_workgroup_get(struct erofs_workgroup *grp) +{ + int o; + +repeat: + o = erofs_wait_on_workgroup_freezed(grp); + if (unlikely(o <= 0)) + return -1; + + if (unlikely(atomic_cmpxchg(&grp->refcount, o, o + 1) != o)) + goto repeat; + + /* decrease refcount paired by erofs_workgroup_put */ + if (unlikely(o == 1)) + atomic_long_dec(&erofs_global_shrink_cnt); + return 0; +} + +struct erofs_workgroup *erofs_find_workgroup(struct super_block *sb, + pgoff_t index, bool *tag) { struct erofs_sb_info *sbi = EROFS_SB(sb); struct erofs_workgroup *grp; - int oldcount; repeat: rcu_read_lock(); @@ -46,15 +65,12 @@ repeat: *tag = xa_pointer_tag(grp); grp = xa_untag_pointer(grp); - if (erofs_workgroup_get(grp, &oldcount)) { + if (erofs_workgroup_get(grp)) { /* prefer to relax rcu read side */ rcu_read_unlock(); goto repeat; } - /* decrease refcount added by erofs_workgroup_put */ - if (unlikely(oldcount == 1)) - atomic_long_dec(&erofs_global_shrink_cnt); DBG_BUGON(index != grp->index); } rcu_read_unlock(); @@ -104,8 +120,6 @@ int erofs_register_workgroup(struct super_block *sb, return err; } -extern void erofs_workgroup_free_rcu(struct erofs_workgroup *grp); - static void __erofs_workgroup_free(struct erofs_workgroup *grp) { atomic_long_dec(&erofs_global_shrink_cnt); @@ -131,9 +145,9 @@ static void erofs_workgroup_unfreeze_final(struct erofs_workgroup *grp) __erofs_workgroup_free(grp); } -bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi, - struct erofs_workgroup *grp, - bool cleanup) +static bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi, + struct erofs_workgroup *grp, + bool cleanup) { /* * for managed cache enabled, the refcount of workgroups @@ -172,9 +186,9 @@ bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi, #else /* for nocache case, no customized reclaim path at all */ -bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi, - struct erofs_workgroup *grp, - bool cleanup) +static bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi, + struct erofs_workgroup *grp, + bool cleanup) { int cnt = atomic_read(&grp->refcount); @@ -256,14 +270,14 @@ void erofs_unregister_super(struct super_block *sb) spin_unlock(&erofs_sb_list_lock); } -unsigned long erofs_shrink_count(struct shrinker *shrink, - struct shrink_control *sc) +static unsigned long erofs_shrink_count(struct shrinker *shrink, + struct shrink_control *sc) { return atomic_long_read(&erofs_global_shrink_cnt); } -unsigned long erofs_shrink_scan(struct shrinker *shrink, - struct shrink_control *sc) +static unsigned long erofs_shrink_scan(struct shrinker *shrink, + struct shrink_control *sc) { struct erofs_sb_info *sbi; struct list_head *p; @@ -319,3 +333,9 @@ unsigned long erofs_shrink_scan(struct shrinker *shrink, return freed; } +struct shrinker erofs_shrinker_info = { + .scan_objects = erofs_shrink_scan, + .count_objects = erofs_shrink_count, + .seeks = DEFAULT_SEEKS, +}; + diff --git a/drivers/staging/erofs/xattr.c b/drivers/staging/erofs/xattr.c @@ -44,19 +44,48 @@ static inline void xattr_iter_end_final(struct xattr_iter *it) static int init_inode_xattrs(struct inode *inode) { + struct erofs_vnode *const vi = EROFS_V(inode); struct xattr_iter it; unsigned int i; struct erofs_xattr_ibody_header *ih; struct super_block *sb; struct erofs_sb_info *sbi; - struct erofs_vnode *vi; bool atomic_map; + int ret = 0; - if (likely(inode_has_inited_xattr(inode))) + /* the most case is that xattrs of this inode are initialized. */ + if (test_bit(EROFS_V_EA_INITED_BIT, &vi->flags)) return 0; - vi = EROFS_V(inode); - BUG_ON(!vi->xattr_isize); + if (wait_on_bit_lock(&vi->flags, EROFS_V_BL_XATTR_BIT, TASK_KILLABLE)) + return -ERESTARTSYS; + + /* someone has initialized xattrs for us? */ + if (test_bit(EROFS_V_EA_INITED_BIT, &vi->flags)) + goto out_unlock; + + /* + * bypass all xattr operations if ->xattr_isize is not greater than + * sizeof(struct erofs_xattr_ibody_header), in detail: + * 1) it is not enough to contain erofs_xattr_ibody_header then + * ->xattr_isize should be 0 (it means no xattr); + * 2) it is just to contain erofs_xattr_ibody_header, which is on-disk + * undefined right now (maybe use later with some new sb feature). + */ + if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) { + errln("xattr_isize %d of nid %llu is not supported yet", + vi->xattr_isize, vi->nid); + ret = -ENOTSUPP; + goto out_unlock; + } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { + if (unlikely(vi->xattr_isize)) { + DBG_BUGON(1); + ret = -EIO; + goto out_unlock; /* xattr ondisk layout error */ + } + ret = -ENOATTR; + goto out_unlock; + } sb = inode->i_sb; sbi = EROFS_SB(sb); @@ -64,8 +93,10 @@ static int init_inode_xattrs(struct inode *inode) it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize); it.page = erofs_get_inline_page(inode, it.blkaddr); - if (IS_ERR(it.page)) - return PTR_ERR(it.page); + if (IS_ERR(it.page)) { + ret = PTR_ERR(it.page); + goto out_unlock; + } /* read in shared xattr array (non-atomic, see kmalloc below) */ it.kaddr = kmap(it.page); @@ -78,7 +109,8 @@ static int init_inode_xattrs(struct inode *inode) sizeof(uint), GFP_KERNEL); if (vi->xattr_shared_xattrs == NULL) { xattr_iter_end(&it, atomic_map); - return -ENOMEM; + ret = -ENOMEM; + goto out_unlock; } /* let's skip ibody header */ @@ -92,8 +124,12 @@ static int init_inode_xattrs(struct inode *inode) it.page = erofs_get_meta_page(sb, ++it.blkaddr, S_ISDIR(inode->i_mode)); - if (IS_ERR(it.page)) - return PTR_ERR(it.page); + if (IS_ERR(it.page)) { + kfree(vi->xattr_shared_xattrs); + vi->xattr_shared_xattrs = NULL; + ret = PTR_ERR(it.page); + goto out_unlock; + } it.kaddr = kmap_atomic(it.page); atomic_map = true; @@ -105,8 +141,11 @@ static int init_inode_xattrs(struct inode *inode) } xattr_iter_end(&it, atomic_map); - inode_set_inited_xattr(inode); - return 0; + set_bit(EROFS_V_EA_INITED_BIT, &vi->flags); + +out_unlock: + clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags); + return ret; } /* @@ -117,10 +156,12 @@ static int init_inode_xattrs(struct inode *inode) * and need to be handled */ struct xattr_iter_handlers { - int (*entry)(struct xattr_iter *, struct erofs_xattr_entry *); - int (*name)(struct xattr_iter *, unsigned int, char *, unsigned int); - int (*alloc_buffer)(struct xattr_iter *, unsigned int); - void (*value)(struct xattr_iter *, unsigned int, char *, unsigned int); + int (*entry)(struct xattr_iter *_it, struct erofs_xattr_entry *entry); + int (*name)(struct xattr_iter *_it, unsigned int processed, char *buf, + unsigned int len); + int (*alloc_buffer)(struct xattr_iter *_it, unsigned int value_sz); + void (*value)(struct xattr_iter *_it, unsigned int processed, char *buf, + unsigned int len); }; static inline int xattr_iter_fixup(struct xattr_iter *it) @@ -422,7 +463,6 @@ static int erofs_xattr_generic_get(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *name, void *buffer, size_t size) { - struct erofs_vnode *const vi = EROFS_V(inode); struct erofs_sb_info *const sbi = EROFS_I_SB(inode); switch (handler->flags) { @@ -440,9 +480,6 @@ static int erofs_xattr_generic_get(const struct xattr_handler *handler, return -EINVAL; } - if (!vi->xattr_isize) - return -ENOATTR; - return erofs_getxattr(inode, handler->flags, name, buffer, size); } @@ -503,8 +540,7 @@ static int xattr_entrylist(struct xattr_iter *_it, if (h == NULL || (h->list != NULL && !h->list(it->dentry))) return 1; - /* Note that at least one of 'prefix' and 'name' should be non-NULL */ - prefix = h->prefix != NULL ? h->prefix : h->name; + prefix = xattr_prefix(h); prefix_len = strlen(prefix); if (it->buffer == NULL) { @@ -627,3 +663,40 @@ ssize_t erofs_listxattr(struct dentry *dentry, return shared_listxattr(&it); } +#ifdef CONFIG_EROFS_FS_POSIX_ACL +struct posix_acl *erofs_get_acl(struct inode *inode, int type) +{ + struct posix_acl *acl; + int prefix, rc; + char *value = NULL; + + switch (type) { + case ACL_TYPE_ACCESS: + prefix = EROFS_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + prefix = EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + default: + return ERR_PTR(-EINVAL); + } + + rc = erofs_getxattr(inode, prefix, "", NULL, 0); + if (rc > 0) { + value = kmalloc(rc, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + rc = erofs_getxattr(inode, prefix, "", value, rc); + } + + if (rc == -ENOATTR) + acl = NULL; + else if (rc < 0) + acl = ERR_PTR(rc); + else + acl = posix_acl_from_xattr(&init_user_ns, value, rc); + kfree(value); + return acl; +} +#endif + diff --git a/drivers/staging/erofs/xattr.h b/drivers/staging/erofs/xattr.h @@ -68,9 +68,7 @@ static const struct xattr_handler *xattr_handler_map[] = { } #ifdef CONFIG_EROFS_FS_XATTR - -extern const struct inode_operations erofs_generic_xattr_iops; -extern const struct inode_operations erofs_dir_xattr_iops; +extern const struct xattr_handler *erofs_xattr_handlers[]; int erofs_getxattr(struct inode *, int, const char *, void *, size_t); ssize_t erofs_listxattr(struct dentry *, char *, size_t); @@ -89,5 +87,11 @@ static ssize_t __maybe_unused erofs_listxattr(struct dentry *dentry, } #endif +#ifdef CONFIG_EROFS_FS_POSIX_ACL +struct posix_acl *erofs_get_acl(struct inode *inode, int type); +#else +#define erofs_get_acl (NULL) +#endif + #endif diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/slab.h> @@ -79,14 +79,14 @@ static int init_display(struct fbtft_par *par) static void reset(struct fbtft_par *par) { - if (par->gpio.reset == -1) + if (!par->gpio.reset) return; dev_dbg(par->info->device, "%s()\n", __func__); - gpio_set_value(par->gpio.reset, 0); + gpiod_set_value(par->gpio.reset, 0); udelay(20); - gpio_set_value(par->gpio.reset, 1); + gpiod_set_value(par->gpio.reset, 1); mdelay(120); } @@ -98,30 +98,30 @@ static int verify_gpios(struct fbtft_par *par) dev_dbg(par->info->device, "%s()\n", __func__); - if (par->EPIN < 0) { + if (!par->EPIN) { dev_err(par->info->device, "Missing info about 'wr' (aka E) gpio. Aborting.\n"); return -EINVAL; } for (i = 0; i < 8; ++i) { - if (par->gpio.db[i] < 0) { + if (!par->gpio.db[i]) { dev_err(par->info->device, "Missing info about 'db[%i]' gpio. Aborting.\n", i); return -EINVAL; } } - if (par->CS0 < 0) { + if (!par->CS0) { dev_err(par->info->device, "Missing info about 'cs0' gpio. Aborting.\n"); return -EINVAL; } - if (par->CS1 < 0) { + if (!par->CS1) { dev_err(par->info->device, "Missing info about 'cs1' gpio. Aborting.\n"); return -EINVAL; } - if (par->RW < 0) { + if (!par->RW) { dev_err(par->info->device, "Missing info about 'rw' gpio. Aborting.\n"); return -EINVAL; @@ -139,22 +139,22 @@ request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio) if (strcasecmp(gpio->name, "wr") == 0) { /* left ks0108 E pin */ par->EPIN = gpio->gpio; - return GPIOF_OUT_INIT_LOW; + return GPIOD_OUT_LOW; } else if (strcasecmp(gpio->name, "cs0") == 0) { /* left ks0108 controller pin */ par->CS0 = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; + return GPIOD_OUT_HIGH; } else if (strcasecmp(gpio->name, "cs1") == 0) { /* right ks0108 controller pin */ par->CS1 = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; + return GPIOD_OUT_HIGH; } /* if write (rw = 0) e(1->0) perform write */ /* if read (rw = 1) e(0->1) set data on D0-7*/ else if (strcasecmp(gpio->name, "rw") == 0) { par->RW = gpio->gpio; - return GPIOF_OUT_INIT_LOW; + return GPIOD_OUT_LOW; } return FBTFT_GPIO_NO_MATCH; @@ -194,15 +194,15 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) /* select chip */ if (*buf) { /* cs1 */ - gpio_set_value(par->CS0, 1); - gpio_set_value(par->CS1, 0); + gpiod_set_value(par->CS0, 1); + gpiod_set_value(par->CS1, 0); } else { /* cs0 */ - gpio_set_value(par->CS0, 0); - gpio_set_value(par->CS1, 1); + gpiod_set_value(par->CS0, 0); + gpiod_set_value(par->CS1, 1); } - gpio_set_value(par->RS, 0); /* RS->0 (command mode) */ + gpiod_set_value(par->RS, 0); /* RS->0 (command mode) */ len--; if (len) { @@ -364,7 +364,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) write_reg(par, 0x00, (0x17 << 3) | (u8)y); /* write bitmap */ - gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ + gpiod_set_value(par->RS, 1); /* RS->1 (data mode) */ ret = par->fbtftops.write(par, buf, len); if (ret < 0) dev_err(par->info->device, @@ -387,7 +387,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) write_reg(par, 0x01, (0x17 << 3) | (u8)y); /* write bitmap */ - gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ + gpiod_set_value(par->RS, 1); /* RS->1 (data mode) */ par->fbtftops.write(par, buf, len); if (ret < 0) dev_err(par->info->device, @@ -397,8 +397,8 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) } kfree(convert_buf); - gpio_set_value(par->CS0, 1); - gpio_set_value(par->CS1, 1); + gpiod_set_value(par->CS0, 1); + gpiod_set_value(par->CS1, 1); return ret; } @@ -408,7 +408,7 @@ static int write(struct fbtft_par *par, void *buf, size_t len) fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, "%s(len=%d): ", __func__, len); - gpio_set_value(par->RW, 0); /* set write mode */ + gpiod_set_value(par->RW, 0); /* set write mode */ while (len--) { u8 i, data; @@ -417,12 +417,12 @@ static int write(struct fbtft_par *par, void *buf, size_t len) /* set data bus */ for (i = 0; i < 8; ++i) - gpio_set_value(par->gpio.db[i], data & (1 << i)); + gpiod_set_value(par->gpio.db[i], data & (1 << i)); /* set E */ - gpio_set_value(par->EPIN, 1); + gpiod_set_value(par->EPIN, 1); udelay(5); /* unset E - write */ - gpio_set_value(par->EPIN, 0); + gpiod_set_value(par->EPIN, 0); udelay(1); } diff --git a/drivers/staging/fbtft/fb_bd663474.c b/drivers/staging/fbtft/fb_bd663474.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -24,8 +24,8 @@ static int init_display(struct fbtft_par *par) { - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ par->fbtftops.reset(par); diff --git a/drivers/staging/fbtft/fb_ili9163.c b/drivers/staging/fbtft/fb_ili9163.c @@ -11,7 +11,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <video/mipi_display.h> @@ -77,8 +77,8 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ write_reg(par, MIPI_DCS_SOFT_RESET); /* software reset */ mdelay(500); diff --git a/drivers/staging/fbtft/fb_ili9320.c b/drivers/staging/fbtft/fb_ili9320.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> diff --git a/drivers/staging/fbtft/fb_ili9325.c b/drivers/staging/fbtft/fb_ili9325.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -85,8 +85,8 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ bt &= 0x07; vc &= 0x07; diff --git a/drivers/staging/fbtft/fb_ili9340.c b/drivers/staging/fbtft/fb_ili9340.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <video/mipi_display.h> diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c @@ -11,7 +11,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -119,7 +119,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) } /* Write data */ - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); ret = par->fbtftops.write(par, par->txbuf.buf, 6 * 84); if (ret < 0) dev_err(par->info->device, "write failed and returned: %d\n", diff --git a/drivers/staging/fbtft/fb_ra8875.c b/drivers/staging/fbtft/fb_ra8875.c @@ -9,7 +9,7 @@ #include <linux/init.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include "fbtft.h" #define DRVNAME "fb_ra8875" @@ -39,7 +39,7 @@ static int write_spi(struct fbtft_par *par, void *buf, size_t len) static int init_display(struct fbtft_par *par) { - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); diff --git a/drivers/staging/fbtft/fb_s6d1121.c b/drivers/staging/fbtft/fb_s6d1121.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -29,8 +29,8 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ /* Initialization sequence from Lib_UTFT */ diff --git a/drivers/staging/fbtft/fb_sh1106.c b/drivers/staging/fbtft/fb_sh1106.c @@ -9,7 +9,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" diff --git a/drivers/staging/fbtft/fb_ssd1289.c b/drivers/staging/fbtft/fb_ssd1289.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include "fbtft.h" @@ -28,8 +28,8 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ write_reg(par, 0x00, 0x0001); write_reg(par, 0x03, 0xA8A4); diff --git a/drivers/staging/fbtft/fb_ssd1305.c b/drivers/staging/fbtft/fb_ssd1305.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -168,7 +168,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) } /* Write data */ - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); ret = par->fbtftops.write(par, par->txbuf.buf, par->info->var.xres * par->info->var.yres / 8); diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -190,7 +190,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) } /* Write data */ - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8); if (ret < 0) dev_err(par->info->device, "write failed and returned: %d\n", diff --git a/drivers/staging/fbtft/fb_ssd1325.c b/drivers/staging/fbtft/fb_ssd1325.c @@ -6,7 +6,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -35,7 +35,7 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - gpio_set_value(par->gpio.cs, 0); + gpiod_set_value(par->gpio.cs, 0); write_reg(par, 0xb3); write_reg(par, 0xf0); @@ -155,7 +155,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) } } - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); /* Write data */ ret = par->fbtftops.write(par, par->txbuf.buf, diff --git a/drivers/staging/fbtft/fb_ssd1331.c b/drivers/staging/fbtft/fb_ssd1331.c @@ -2,7 +2,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -80,8 +80,8 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) va_start(args, len); *buf = (u8)va_arg(args, unsigned int); - if (par->gpio.dc != -1) - gpio_set_value(par->gpio.dc, 0); + if (!par->gpio.dc) + gpiod_set_value(par->gpio.dc, 0); ret = par->fbtftops.write(par, par->buf, sizeof(u8)); if (ret < 0) { va_end(args); @@ -103,8 +103,8 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) return; } } - if (par->gpio.dc != -1) - gpio_set_value(par->gpio.dc, 1); + if (!par->gpio.dc) + gpiod_set_value(par->gpio.dc, 1); va_end(args); } diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c @@ -2,7 +2,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -164,7 +164,7 @@ static int set_gamma(struct fbtft_par *par, u32 *curves) static int blank(struct fbtft_par *par, bool on) { fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n", - __func__, on ? "true" : "false"); + __func__, on ? "true" : "false"); if (on) write_reg(par, 0xAE); else diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c @@ -38,7 +38,7 @@ static int init_display(struct fbtft_par *par) write_reg(par, 0xE5, 0x00); write_reg(par, 0xF0, 0x36, 0xA5, 0x53); write_reg(par, 0xE0, 0x00, 0x35, 0x33, 0x00, 0x00, 0x00, - 0x00, 0x35, 0x33, 0x00, 0x00, 0x00); + 0x00, 0x35, 0x33, 0x00, 0x00, 0x00); write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, 0x55); write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE); udelay(250); diff --git a/drivers/staging/fbtft/fb_tls8204.c b/drivers/staging/fbtft/fb_tls8204.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -94,7 +94,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) /* The display is 102x68 but the LCD is 84x48. * Set the write pointer at the start of each row. */ - gpio_set_value(par->gpio.dc, 0); + gpiod_set_value(par->gpio.dc, 0); write_reg(par, 0x80 | 0); write_reg(par, 0x40 | y); @@ -109,7 +109,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) *buf++ = ch; } /* Write the row */ - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); if (ret < 0) { dev_err(par->info->device, diff --git a/drivers/staging/fbtft/fb_uc1611.c b/drivers/staging/fbtft/fb_uc1611.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -251,7 +251,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) } break; } - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); /* Write data */ ret = par->fbtftops.write(par, par->txbuf.buf, len / 2); diff --git a/drivers/staging/fbtft/fb_uc1701.c b/drivers/staging/fbtft/fb_uc1701.c @@ -11,7 +11,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -136,9 +136,9 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) write_reg(par, LCD_PAGE_ADDRESS | (u8)y); write_reg(par, 0x00); write_reg(par, LCD_COL_ADDRESS); - gpio_set_value(par->gpio.dc, 1); + gpiod_set_value(par->gpio.dc, 1); ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); - gpio_set_value(par->gpio.dc, 0); + gpiod_set_value(par->gpio.dc, 0); } if (ret < 0) diff --git a/drivers/staging/fbtft/fb_upd161704.c b/drivers/staging/fbtft/fb_upd161704.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -26,8 +26,8 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ /* Initialization sequence from Lib_UTFT */ diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include "fbtft.h" @@ -213,7 +213,7 @@ static int set_var(struct fbtft_par *par) static int verify_gpios(struct fbtft_par *par) { - if (par->gpio.reset < 0) { + if (!par->gpio.reset) { dev_err(par->info->device, "Missing 'reset' gpio. Aborting.\n"); return -EINVAL; } diff --git a/drivers/staging/fbtft/fbtft-bus.c b/drivers/staging/fbtft/fbtft-bus.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/export.h> #include <linux/errno.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include "fbtft.h" @@ -135,8 +135,8 @@ int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len) remain = len / 2; vmem16 = (u16 *)(par->info->screen_buffer + offset); - if (par->gpio.dc != -1) - gpio_set_value(par->gpio.dc, 1); + if (!par->gpio.dc) + gpiod_set_value(par->gpio.dc, 1); /* non buffered write */ if (!par->txbuf.buf) diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c @@ -16,7 +16,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/fb.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> #include <linux/uaccess.h> @@ -24,7 +24,6 @@ #include <linux/platform_device.h> #include <linux/spinlock.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <video/mipi_display.h> #include "fbtft.h" @@ -38,8 +37,8 @@ int fbtft_write_buf_dc(struct fbtft_par *par, void *buf, size_t len, int dc) { int ret; - if (gpio_is_valid(par->gpio.dc)) - gpio_set_value(par->gpio.dc, dc); + if (par->gpio.dc) + gpiod_set_value(par->gpio.dc, dc); ret = par->fbtftops.write(par, buf, len); if (ret < 0) @@ -71,127 +70,26 @@ void fbtft_dbg_hex(const struct device *dev, int groupsize, } EXPORT_SYMBOL(fbtft_dbg_hex); -static unsigned long fbtft_request_gpios_match(struct fbtft_par *par, - const struct fbtft_gpio *gpio) -{ - int ret; - unsigned int val; - - fbtft_par_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, "%s('%s')\n", - __func__, gpio->name); - - if (strcasecmp(gpio->name, "reset") == 0) { - par->gpio.reset = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; - } else if (strcasecmp(gpio->name, "dc") == 0) { - par->gpio.dc = gpio->gpio; - return GPIOF_OUT_INIT_LOW; - } else if (strcasecmp(gpio->name, "cs") == 0) { - par->gpio.cs = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; - } else if (strcasecmp(gpio->name, "wr") == 0) { - par->gpio.wr = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; - } else if (strcasecmp(gpio->name, "rd") == 0) { - par->gpio.rd = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; - } else if (strcasecmp(gpio->name, "latch") == 0) { - par->gpio.latch = gpio->gpio; - return GPIOF_OUT_INIT_LOW; - } else if (gpio->name[0] == 'd' && gpio->name[1] == 'b') { - ret = kstrtouint(&gpio->name[2], 10, &val); - if (ret == 0 && val < 16) { - par->gpio.db[val] = gpio->gpio; - return GPIOF_OUT_INIT_LOW; - } - } else if (strcasecmp(gpio->name, "led") == 0) { - par->gpio.led[0] = gpio->gpio; - return GPIOF_OUT_INIT_LOW; - } else if (strcasecmp(gpio->name, "led_") == 0) { - par->gpio.led[0] = gpio->gpio; - return GPIOF_OUT_INIT_HIGH; - } - - return FBTFT_GPIO_NO_MATCH; -} - -static int fbtft_request_gpios(struct fbtft_par *par) -{ - struct fbtft_platform_data *pdata = par->pdata; - const struct fbtft_gpio *gpio; - unsigned long flags; - int ret; - - if (!(pdata && pdata->gpios)) - return 0; - - gpio = pdata->gpios; - while (gpio->name[0]) { - flags = FBTFT_GPIO_NO_MATCH; - /* if driver provides match function, try it first, - * if no match use our own - */ - if (par->fbtftops.request_gpios_match) - flags = par->fbtftops.request_gpios_match(par, gpio); - if (flags == FBTFT_GPIO_NO_MATCH) - flags = fbtft_request_gpios_match(par, gpio); - if (flags != FBTFT_GPIO_NO_MATCH) { - ret = devm_gpio_request_one(par->info->device, - gpio->gpio, flags, - par->info->device->driver->name); - if (ret < 0) { - dev_err(par->info->device, - "%s: gpio_request_one('%s'=%d) failed with %d\n", - __func__, gpio->name, - gpio->gpio, ret); - return ret; - } - fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, - "%s: '%s' = GPIO%d\n", - __func__, gpio->name, gpio->gpio); - } - gpio++; - } - - return 0; -} - #ifdef CONFIG_OF static int fbtft_request_one_gpio(struct fbtft_par *par, - const char *name, int index, int *gpiop) + const char *name, int index, + struct gpio_desc **gpiop) { struct device *dev = par->info->device; struct device_node *node = dev->of_node; - int gpio, flags, ret = 0; - enum of_gpio_flags of_flags; + int ret = 0; if (of_find_property(node, name, NULL)) { - gpio = of_get_named_gpio_flags(node, name, index, &of_flags); - if (gpio == -ENOENT) - return 0; - if (gpio == -EPROBE_DEFER) - return gpio; - if (gpio < 0) { - dev_err(dev, - "failed to get '%s' from DT\n", name); - return gpio; - } - - /* active low translates to initially low */ - flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : - GPIOF_OUT_INIT_HIGH; - ret = devm_gpio_request_one(dev, gpio, flags, - dev->driver->name); - if (ret) { + *gpiop = devm_gpiod_get_index(dev, dev->driver->name, index, + GPIOD_OUT_HIGH); + if (IS_ERR(*gpiop)) { + ret = PTR_ERR(*gpiop); dev_err(dev, - "gpio_request_one('%s'=%d) failed with %d\n", - name, gpio, ret); + "Failed to request %s GPIO:%d\n", name, ret); return ret; } - if (gpiop) - *gpiop = gpio; - fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", - __func__, name, gpio); + fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' GPIO\n", + __func__, name); } return ret; @@ -254,9 +152,9 @@ static int fbtft_backlight_update_status(struct backlight_device *bd) if ((bd->props.power == FB_BLANK_UNBLANK) && (bd->props.fb_blank == FB_BLANK_UNBLANK)) - gpio_set_value(par->gpio.led[0], polarity); + gpiod_set_value(par->gpio.led[0], polarity); else - gpio_set_value(par->gpio.led[0], !polarity); + gpiod_set_value(par->gpio.led[0], !polarity); return 0; } @@ -286,7 +184,7 @@ void fbtft_register_backlight(struct fbtft_par *par) struct backlight_device *bd; struct backlight_properties bl_props = { 0, }; - if (par->gpio.led[0] == -1) { + if (!par->gpio.led[0]) { fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s(): led pin not set, exiting.\n", __func__); return; @@ -295,7 +193,7 @@ void fbtft_register_backlight(struct fbtft_par *par) bl_props.type = BACKLIGHT_RAW; /* Assume backlight is off, get polarity from current state of pin */ bl_props.power = FB_BLANK_POWERDOWN; - if (!gpio_get_value(par->gpio.led[0])) + if (!gpiod_get_value(par->gpio.led[0])) par->polarity = true; bd = backlight_device_register(dev_driver_string(par->info->device), @@ -333,12 +231,12 @@ static void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, static void fbtft_reset(struct fbtft_par *par) { - if (par->gpio.reset == -1) + if (!par->gpio.reset) return; fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__); - gpio_set_value_cansleep(par->gpio.reset, 0); + gpiod_set_value_cansleep(par->gpio.reset, 0); usleep_range(20, 40); - gpio_set_value_cansleep(par->gpio.reset, 1); + gpiod_set_value_cansleep(par->gpio.reset, 1); msleep(120); } @@ -538,9 +436,9 @@ static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) return chan << bf->offset; } -static int fbtft_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, - unsigned int blue, unsigned int transp, - struct fb_info *info) +static int fbtft_fb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) { unsigned int val; int ret = 1; @@ -663,7 +561,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, int txbuflen = display->txbuflen; unsigned int bpp = display->bpp; unsigned int fps = display->fps; - int vmem_size, i; + int vmem_size; const s16 *init_sequence = display->init_sequence; char *gamma = display->gamma; u32 *gamma_curves = NULL; @@ -841,19 +739,6 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, par->txbuf.len = txbuflen; } - /* Initialize gpios to disabled */ - par->gpio.reset = -1; - par->gpio.dc = -1; - par->gpio.rd = -1; - par->gpio.wr = -1; - par->gpio.cs = -1; - par->gpio.latch = -1; - for (i = 0; i < 16; i++) { - par->gpio.db[i] = -1; - par->gpio.led[i] = -1; - par->gpio.aux[i] = -1; - } - /* default fbtft operations */ par->fbtftops.write = fbtft_write_spi; par->fbtftops.read = fbtft_read_spi; @@ -863,7 +748,6 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, par->fbtftops.reset = fbtft_reset; par->fbtftops.mkdirty = fbtft_mkdirty; par->fbtftops.update_display = fbtft_update_display; - par->fbtftops.request_gpios = fbtft_request_gpios; if (display->backlight) par->fbtftops.register_backlight = fbtft_register_backlight; @@ -1035,8 +919,8 @@ static int fbtft_init_display_dt(struct fbtft_par *par) return -EINVAL; par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ while (p) { if (val & FBTFT_OF_INIT_CMD) { @@ -1126,8 +1010,8 @@ int fbtft_init_display(struct fbtft_par *par) } par->fbtftops.reset(par); - if (par->gpio.cs != -1) - gpio_set_value(par->gpio.cs, 0); /* Activate chip */ + if (!par->gpio.cs) + gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ i = 0; while (i < FBTFT_MAX_INIT_SEQUENCE) { @@ -1227,7 +1111,7 @@ static int fbtft_verify_gpios(struct fbtft_par *par) fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); if (pdata->display.buswidth != 9 && par->startbyte == 0 && - par->gpio.dc < 0) { + !par->gpio.dc) { dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n"); return -EINVAL; @@ -1236,12 +1120,12 @@ static int fbtft_verify_gpios(struct fbtft_par *par) if (!par->pdev) return 0; - if (par->gpio.wr < 0) { + if (!par->gpio.wr) { dev_err(par->info->device, "Missing 'wr' gpio. Aborting.\n"); return -EINVAL; } for (i = 0; i < pdata->display.buswidth; i++) { - if (par->gpio.db[i] < 0) { + if (!par->gpio.db[i]) { dev_err(par->info->device, "Missing 'db%02d' gpio. Aborting.\n", i); return -EINVAL; diff --git a/drivers/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/export.h> #include <linux/errno.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include "fbtft.h" @@ -142,30 +142,30 @@ int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len) data = *(u8 *)buf; /* Start writing by pulling down /WR */ - gpio_set_value(par->gpio.wr, 0); + gpiod_set_value(par->gpio.wr, 0); /* Set data */ #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO if (data == prev_data) { - gpio_set_value(par->gpio.wr, 0); /* used as delay */ + gpiod_set_value(par->gpio.wr, 0); /* used as delay */ } else { for (i = 0; i < 8; i++) { if ((data & 1) != (prev_data & 1)) - gpio_set_value(par->gpio.db[i], - data & 1); + gpiod_set_value(par->gpio.db[i], + data & 1); data >>= 1; prev_data >>= 1; } } #else for (i = 0; i < 8; i++) { - gpio_set_value(par->gpio.db[i], data & 1); + gpiod_set_value(par->gpio.db[i], data & 1); data >>= 1; } #endif /* Pullup /WR */ - gpio_set_value(par->gpio.wr, 1); + gpiod_set_value(par->gpio.wr, 1); #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO prev_data = *(u8 *)buf; @@ -192,30 +192,30 @@ int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len) data = *(u16 *)buf; /* Start writing by pulling down /WR */ - gpio_set_value(par->gpio.wr, 0); + gpiod_set_value(par->gpio.wr, 0); /* Set data */ #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO if (data == prev_data) { - gpio_set_value(par->gpio.wr, 0); /* used as delay */ + gpiod_set_value(par->gpio.wr, 0); /* used as delay */ } else { for (i = 0; i < 16; i++) { if ((data & 1) != (prev_data & 1)) - gpio_set_value(par->gpio.db[i], - data & 1); + gpiod_set_value(par->gpio.db[i], + data & 1); data >>= 1; prev_data >>= 1; } } #else for (i = 0; i < 16; i++) { - gpio_set_value(par->gpio.db[i], data & 1); + gpiod_set_value(par->gpio.db[i], data & 1); data >>= 1; } #endif /* Pullup /WR */ - gpio_set_value(par->gpio.wr, 1); + gpiod_set_value(par->gpio.wr, 1); #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO prev_data = *(u16 *)buf; diff --git a/drivers/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h @@ -27,7 +27,7 @@ */ struct fbtft_gpio { char name[FBTFT_GPIO_NAME_SIZE]; - unsigned int gpio; + struct gpio_desc *gpio; }; struct fbtft_par; @@ -134,7 +134,6 @@ struct fbtft_display { */ struct fbtft_platform_data { struct fbtft_display display; - const struct fbtft_gpio *gpios; unsigned int rotate; bool bgr; unsigned int fps; @@ -207,15 +206,15 @@ struct fbtft_par { unsigned int dirty_lines_start; unsigned int dirty_lines_end; struct { - int reset; - int dc; - int rd; - int wr; - int latch; - int cs; - int db[16]; - int led[16]; - int aux[16]; + struct gpio_desc *reset; + struct gpio_desc *dc; + struct gpio_desc *rd; + struct gpio_desc *wr; + struct gpio_desc *latch; + struct gpio_desc *cs; + struct gpio_desc *db[16]; + struct gpio_desc *led[16]; + struct gpio_desc *aux[16]; } gpio; const s16 *init_sequence; struct { diff --git a/drivers/staging/fbtft/fbtft_device.c b/drivers/staging/fbtft/fbtft_device.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <video/mipi_display.h> @@ -45,11 +45,6 @@ static int mode = -1; module_param(mode, int, 0000); MODULE_PARM_DESC(mode, "SPI mode (override device default)"); -static char *gpios; -module_param(gpios, charp, 0000); -MODULE_PARM_DESC(gpios, - "List of gpios. Comma separated with the form: reset:23,dc:24 (when overriding the default, all gpios must be specified)"); - static unsigned int fps; module_param(fps, uint, 0000); MODULE_PARM_DESC(fps, "Frames per second (override driver default)"); @@ -101,7 +96,7 @@ MODULE_PARM_DESC(debug, static unsigned int verbose = 3; module_param(verbose, uint, 0000); MODULE_PARM_DESC(verbose, - "0 silent, >0 show gpios, >1 show devices, >2 show devices before (default=3)"); + "0 silent, >1 show devices, >2 show devices before (default=3)"); struct fbtft_device_display { char *name; @@ -279,12 +274,6 @@ static struct fbtft_device_display displays[] = { .buswidth = 8, .backlight = 1, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, .gamma = ADAFRUIT18_GAMMA, } } @@ -302,12 +291,6 @@ static struct fbtft_device_display displays[] = { adafruit18_green_tab_set_addr_win, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, .gamma = ADAFRUIT18_GAMMA, } } @@ -323,11 +306,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "led", 23 }, - {}, - }, } } }, { @@ -342,12 +320,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, } } }, { @@ -362,12 +334,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, } } }, { @@ -380,11 +346,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, } } }, { @@ -399,12 +360,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, .init_sequence = cberry28_init_sequence, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 22 }, - { "led", 18 }, - {}, - }, .gamma = CBERRY28_GAMMA, } } @@ -420,9 +375,6 @@ static struct fbtft_device_display displays[] = { .buswidth = 8, .backlight = FBTFT_ONBOARD_BACKLIGHT, }, - .gpios = (const struct fbtft_gpio []) { - {}, - }, }, } } @@ -437,11 +389,6 @@ static struct fbtft_device_display displays[] = { .buswidth = 8, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 13 }, - { "dc", 6 }, - {}, - }, } } }, { @@ -458,11 +405,6 @@ static struct fbtft_device_display displays[] = { .height = 272, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, } } }, { @@ -479,11 +421,6 @@ static struct fbtft_device_display displays[] = { .height = 480, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, } } }, { @@ -496,10 +433,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "dc", 24 }, - {}, - }, } } }, { @@ -512,9 +445,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 9, }, - .gpios = (const struct fbtft_gpio []) { - {}, - }, } } }, { @@ -523,13 +453,6 @@ static struct fbtft_device_display displays[] = { .modalias = "flexfb", .max_speed_hz = 32000000, .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, - } } }, { .name = "flexpfb", @@ -538,24 +461,6 @@ static struct fbtft_device_display displays[] = { .id = 0, .dev = { .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .gpios = (const struct fbtft_gpio []) { - { "reset", 17 }, - { "dc", 1 }, - { "wr", 0 }, - { "cs", 21 }, - { "db00", 9 }, - { "db01", 11 }, - { "db02", 18 }, - { "db03", 23 }, - { "db04", 24 }, - { "db05", 25 }, - { "db06", 8 }, - { "db07", 7 }, - { "led", 4 }, - {}, - }, - }, } } }, { @@ -570,11 +475,6 @@ static struct fbtft_device_display displays[] = { .backlight = FBTFT_ONBOARD_BACKLIGHT, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 24 }, - { "dc", 25 }, - {}, - }, } } }, { @@ -588,12 +488,6 @@ static struct fbtft_device_display displays[] = { .buswidth = 8, .backlight = 1, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 23 }, - {}, - }, } } }, { @@ -609,11 +503,6 @@ static struct fbtft_device_display displays[] = { }, .startbyte = 0x70, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "led", 18 }, - {}, - }, } } }, { @@ -631,11 +520,6 @@ static struct fbtft_device_display displays[] = { .startbyte = 0x70, .bgr = true, .fps = 50, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "led", 18 }, - {}, - }, .gamma = HY28B_GAMMA, } } @@ -652,12 +536,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 22 }, - {}, - }, } } }, { @@ -673,22 +551,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = false, - .gpios = (const struct fbtft_gpio []) { - /* Wiring for LCD adapter kit */ - { "reset", 7 }, - { "dc", 0 }, /* rev 2: 2 */ - { "wr", 1 }, /* rev 2: 3 */ - { "cs", 8 }, - { "db00", 17 }, - { "db01", 18 }, - { "db02", 21 }, /* rev 2: 27 */ - { "db03", 22 }, - { "db04", 23 }, - { "db05", 24 }, - { "db06", 25 }, - { "db07", 4 }, - {} - }, }, } } @@ -705,9 +567,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - {}, - }, }, } } @@ -723,11 +582,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, } } }, { @@ -743,12 +597,6 @@ static struct fbtft_device_display displays[] = { }, .startbyte = 0x70, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, } } }, { @@ -763,11 +611,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "led", 18 }, - {}, - }, } } }, { @@ -777,10 +620,6 @@ static struct fbtft_device_display displays[] = { .max_speed_hz = 4000000, .mode = SPI_MODE_3, .platform_data = &(struct fbtft_platform_data) { - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - {}, - }, } } }, { @@ -793,12 +632,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 23 }, - {}, - }, } } }, { @@ -811,12 +644,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 23 }, - {}, - }, } } }, { @@ -831,9 +658,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - {}, - }, } } }, { @@ -849,12 +673,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 22 }, - {}, - }, } } }, { @@ -871,10 +689,6 @@ static struct fbtft_device_display displays[] = { .init_sequence = pitft_init_sequence, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "dc", 25 }, - {}, - }, } } }, { @@ -888,11 +702,6 @@ static struct fbtft_device_display displays[] = { .buswidth = 8, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 24 }, - { "dc", 25 }, - {}, - }, .gamma = PIOLED_GAMMA } } @@ -908,12 +717,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 23 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, } } }, { @@ -928,12 +731,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 23 }, - {}, - }, } } }, { @@ -946,11 +743,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, } } }, { @@ -968,9 +760,6 @@ static struct fbtft_device_display displays[] = { .fbtftops.write = write_gpio16_wr_slow, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - {}, - }, }, }, } @@ -988,9 +777,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - {}, - }, }, }, } @@ -1010,9 +796,6 @@ static struct fbtft_device_display displays[] = { fbtft_write_gpio16_wr_latched, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - {}, - }, }, }, } @@ -1028,11 +811,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - {}, - }, } } }, { @@ -1044,9 +822,6 @@ static struct fbtft_device_display displays[] = { .chip_select = 0, .mode = SPI_MODE_0, .platform_data = &(struct fbtft_platform_data) { - .gpios = (const struct fbtft_gpio []) { - {}, - }, } } }, { @@ -1059,11 +834,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 24 }, - { "dc", 25 }, - {}, - }, } } }, { @@ -1078,12 +848,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, } } }, { @@ -1098,12 +862,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 25 }, - { "dc", 24 }, - { "led", 18 }, - {}, - }, } } }, { @@ -1118,12 +876,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 15 }, - { "dc", 25 }, - { "led_", 18 }, - {}, - }, } } }, { @@ -1138,12 +890,6 @@ static struct fbtft_device_display displays[] = { .backlight = 1, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 15 }, - { "dc", 25 }, - { "led_", 18 }, - {}, - }, } } }, { @@ -1156,11 +902,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 24 }, - { "dc", 25 }, - {}, - }, } } }, { @@ -1177,11 +918,6 @@ static struct fbtft_device_display displays[] = { waveshare32b_init_sequence, }, .bgr = true, - .gpios = (const struct fbtft_gpio []) { - { "reset", 27 }, - { "dc", 22 }, - {}, - }, } } }, { @@ -1194,11 +930,6 @@ static struct fbtft_device_display displays[] = { .display = { .buswidth = 8, }, - .gpios = (const struct fbtft_gpio []) { - { "reset", 24 }, - { "dc", 25 }, - {}, - }, } } }, { @@ -1211,9 +942,6 @@ static struct fbtft_device_display displays[] = { .max_speed_hz = 0, .mode = SPI_MODE_0, .platform_data = &(struct fbtft_platform_data) { - .gpios = (const struct fbtft_gpio []) { - {}, - }, } }, .pdev = &(struct platform_device) { @@ -1222,9 +950,6 @@ static struct fbtft_device_display displays[] = { .dev = { .release = fbtft_device_pdev_release, .platform_data = &(struct fbtft_platform_data) { - .gpios = (const struct fbtft_gpio []) { - {}, - }, }, }, }, @@ -1246,30 +971,30 @@ static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len) data = *(u16 *)buf; /* Start writing by pulling down /WR */ - gpio_set_value(par->gpio.wr, 0); + gpiod_set_value(par->gpio.wr, 0); /* Set data */ #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO if (data == prev_data) { - gpio_set_value(par->gpio.wr, 0); /* used as delay */ + gpiod_set_value(par->gpio.wr, 0); /* used as delay */ } else { for (i = 0; i < 16; i++) { if ((data & 1) != (prev_data & 1)) - gpio_set_value(par->gpio.db[i], - data & 1); + gpiod_set_value(par->gpio.db[i], + data & 1); data >>= 1; prev_data >>= 1; } } #else for (i = 0; i < 16; i++) { - gpio_set_value(par->gpio.db[i], data & 1); + gpiod_set_value(par->gpio.db[i], data & 1); data >>= 1; } #endif /* Pullup /WR */ - gpio_set_value(par->gpio.wr, 1); + gpiod_set_value(par->gpio.wr, 1); #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO prev_data = *(u16 *)buf; @@ -1289,9 +1014,6 @@ static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, write_reg(par, 0x2C); } -/* used if gpios parameter is present */ -static struct fbtft_gpio fbtft_device_param_gpios[MAX_GPIOS + 1] = { }; - static void fbtft_device_pdev_release(struct device *dev) { /* Needed to silence this message: @@ -1382,11 +1104,8 @@ static int __init fbtft_device_init(void) { struct spi_board_info *spi = NULL; struct fbtft_platform_data *pdata; - const struct fbtft_gpio *gpio = NULL; - char *p_gpio, *p_name, *p_num; bool found = false; int i = 0; - long val; int ret = 0; if (!name) { @@ -1404,38 +1123,6 @@ static int __init fbtft_device_init(void) return -EINVAL; } - /* parse module parameter: gpios */ - while ((p_gpio = strsep(&gpios, ","))) { - if (!strchr(p_gpio, ':')) { - pr_err("error: missing ':' in gpios parameter: %s\n", - p_gpio); - return -EINVAL; - } - p_num = p_gpio; - p_name = strsep(&p_num, ":"); - if (!p_name || !p_num) { - pr_err("something bad happened parsing gpios parameter: %s\n", - p_gpio); - return -EINVAL; - } - ret = kstrtol(p_num, 10, &val); - if (ret) { - pr_err("could not parse number in gpios parameter: %s:%s\n", - p_name, p_num); - return -EINVAL; - } - strncpy(fbtft_device_param_gpios[i].name, p_name, - FBTFT_GPIO_NAME_SIZE - 1); - fbtft_device_param_gpios[i++].gpio = (int)val; - if (i == MAX_GPIOS) { - pr_err("gpios parameter: exceeded max array size: %d\n", - MAX_GPIOS); - return -EINVAL; - } - } - if (fbtft_device_param_gpios[0].name[0]) - gpio = fbtft_device_param_gpios; - if (verbose > 2) { pr_spi_devices(); /* print list of registered SPI devices */ pr_p_devices(); /* print list of 'fb' platform devices */ @@ -1516,8 +1203,6 @@ static int __init fbtft_device_init(void) pdata->txbuflen = txbuflen; if (init_num) pdata->display.init_sequence = init; - if (gpio) - pdata->gpios = gpio; if (custom) { pdata->display.width = width; pdata->display.height = height; @@ -1549,19 +1234,6 @@ static int __init fbtft_device_init(void) return -EINVAL; } - if (verbose && pdata && pdata->gpios) { - gpio = pdata->gpios; - pr_info("GPIOS used by '%s':\n", name); - found = false; - while (verbose && gpio->name[0]) { - pr_info("'%s' = GPIO%d\n", gpio->name, gpio->gpio); - gpio++; - found = true; - } - if (!found) - pr_info("(none)\n"); - } - if (spi_device && (verbose > 1)) pr_spi_devices(); if (p_device && (verbose > 1)) diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/vmalloc.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -521,7 +521,7 @@ static int flexfb_verify_gpios_dc(struct fbtft_par *par) { fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); - if (par->gpio.dc < 0) { + if (!par->gpio.dc) { dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n"); return -EINVAL; @@ -537,22 +537,22 @@ static int flexfb_verify_gpios_db(struct fbtft_par *par) fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); - if (par->gpio.dc < 0) { + if (!par->gpio.dc) { dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n"); return -EINVAL; } - if (par->gpio.wr < 0) { + if (!par->gpio.wr) { dev_err(par->info->device, "Missing info about 'wr' gpio. Aborting.\n"); return -EINVAL; } - if (latched && (par->gpio.latch < 0)) { + if (latched && !par->gpio.latch) { dev_err(par->info->device, "Missing info about 'latch' gpio. Aborting.\n"); return -EINVAL; } if (latched) num_db = buswidth / 2; for (i = 0; i < num_db; i++) { - if (par->gpio.db[i] < 0) { + if (!par->gpio.db[i]) { dev_err(par->info->device, "Missing info about 'db%02d' gpio. Aborting.\n", i); diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h b/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright 2014-2016 Freescale Semiconductor Inc. * Copyright 2017-2018 NXP diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw.h b/drivers/staging/fsl-dpaa2/ethsw/dpsw.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright 2014-2016 Freescale Semiconductor Inc. * Copyright 2017-2018 NXP diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.h b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * DPAA2 Ethernet Switch declarations * diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c @@ -1213,6 +1213,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct fwtty_port *port = tty->driver_data; + mutex_lock(&port->port.mutex); ss->type = PORT_UNKNOWN; ss->line = port->port.tty->index; diff --git a/drivers/staging/gasket/gasket_interrupt.c b/drivers/staging/gasket/gasket_interrupt.c @@ -9,7 +9,6 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/printk.h> -#include <linux/version.h> #ifdef GASKET_KERNEL_TRACE_SUPPORT #define CREATE_TRACE_POINTS #include <trace/events/gasket_interrupt.h> diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c @@ -4,16 +4,6 @@ * * Copyright (C) 2007 Google, Inc. * Copyright (C) 2012 Intel, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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> diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig @@ -148,6 +148,7 @@ if GREYBUS_BRIDGED_PHY config GREYBUS_GPIO tristate "Greybus GPIO Bridged PHY driver" depends on GPIOLIB + select GPIOLIB_IRQCHIP ---help--- Select this option if you have a device that follows the Greybus GPIO Bridged PHY Class specification. diff --git a/drivers/staging/greybus/TODO b/drivers/staging/greybus/TODO @@ -1,5 +1,3 @@ * Convert all uses of the old GPIO API from <linux/gpio.h> to the GPIO descriptor API in <linux/gpio/consumer.h> and look up GPIO lines from device tree or ACPI. -* Convert the GPIO driver to use the GPIO irqchip library - GPIOLIB_IRQCHIP instead of reimplementing the same. diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c @@ -8,9 +8,8 @@ #include <linux/clk.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> -#include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/module.h> #include <linux/pinctrl/consumer.h> @@ -24,12 +23,12 @@ static void apb_bootret_deassert(struct device *dev); struct arche_apb_ctrl_drvdata { /* Control GPIO signals to and from AP <=> AP Bridges */ - int resetn_gpio; - int boot_ret_gpio; - int pwroff_gpio; - int wake_in_gpio; - int wake_out_gpio; - int pwrdn_gpio; + struct gpio_desc *resetn; + struct gpio_desc *boot_ret; + struct gpio_desc *pwroff; + struct gpio_desc *wake_in; + struct gpio_desc *wake_out; + struct gpio_desc *pwrdn; enum arche_platform_state state; bool init_disabled; @@ -37,28 +36,28 @@ struct arche_apb_ctrl_drvdata { struct regulator *vcore; struct regulator *vio; - int clk_en_gpio; + struct gpio_desc *clk_en; struct clk *clk; struct pinctrl *pinctrl; struct pinctrl_state *pin_default; /* V2: SPI Bus control */ - int spi_en_gpio; + struct gpio_desc *spi_en; bool spi_en_polarity_high; }; /* * Note that these low level api's are active high */ -static inline void deassert_reset(unsigned int gpio) +static inline void deassert_reset(struct gpio_desc *gpio) { - gpio_set_value(gpio, 1); + gpiod_set_raw_value(gpio, 1); } -static inline void assert_reset(unsigned int gpio) +static inline void assert_reset(struct gpio_desc *gpio) { - gpio_set_value(gpio, 0); + gpiod_set_raw_value(gpio, 0); } /* @@ -75,11 +74,10 @@ static int coldboot_seq(struct platform_device *pdev) return 0; /* Hold APB in reset state */ - assert_reset(apb->resetn_gpio); + assert_reset(apb->resetn); - if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && - gpio_is_valid(apb->spi_en_gpio)) - devm_gpio_free(dev, apb->spi_en_gpio); + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && apb->spi_en) + devm_gpiod_put(dev, apb->spi_en); /* Enable power to APB */ if (!IS_ERR(apb->vcore)) { @@ -101,13 +99,13 @@ static int coldboot_seq(struct platform_device *pdev) apb_bootret_deassert(dev); /* On DB3 clock was not mandatory */ - if (gpio_is_valid(apb->clk_en_gpio)) - gpio_set_value(apb->clk_en_gpio, 1); + if (apb->clk_en) + gpiod_set_value(apb->clk_en, 1); usleep_range(100, 200); /* deassert reset to APB : Active-low signal */ - deassert_reset(apb->resetn_gpio); + deassert_reset(apb->resetn); apb->state = ARCHE_PLATFORM_STATE_ACTIVE; @@ -136,25 +134,25 @@ static int fw_flashing_seq(struct platform_device *pdev) return ret; } - if (gpio_is_valid(apb->spi_en_gpio)) { + if (apb->spi_en) { unsigned long flags; if (apb->spi_en_polarity_high) - flags = GPIOF_OUT_INIT_HIGH; + flags = GPIOD_OUT_HIGH; else - flags = GPIOF_OUT_INIT_LOW; + flags = GPIOD_OUT_LOW; - ret = devm_gpio_request_one(dev, apb->spi_en_gpio, - flags, "apb_spi_en"); - if (ret) { - dev_err(dev, "Failed requesting SPI bus en gpio %d\n", - apb->spi_en_gpio); + apb->spi_en = devm_gpiod_get(dev, "spi-en", flags); + if (IS_ERR(apb->spi_en)) { + ret = PTR_ERR(apb->spi_en); + dev_err(dev, "Failed requesting SPI bus en GPIO: %d\n", + ret); return ret; } } /* for flashing device should be in reset state */ - assert_reset(apb->resetn_gpio); + assert_reset(apb->resetn); apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING; return 0; @@ -176,9 +174,8 @@ static int standby_boot_seq(struct platform_device *pdev) apb->state == ARCHE_PLATFORM_STATE_OFF) return 0; - if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && - gpio_is_valid(apb->spi_en_gpio)) - devm_gpio_free(dev, apb->spi_en_gpio); + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && apb->spi_en) + devm_gpiod_put(dev, apb->spi_en); /* * As per WDM spec, do nothing @@ -201,13 +198,12 @@ static void poweroff_seq(struct platform_device *pdev) if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF) return; - if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && - gpio_is_valid(apb->spi_en_gpio)) - devm_gpio_free(dev, apb->spi_en_gpio); + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && apb->spi_en) + devm_gpiod_put(dev, apb->spi_en); /* disable the clock */ - if (gpio_is_valid(apb->clk_en_gpio)) - gpio_set_value(apb->clk_en_gpio, 0); + if (apb->clk_en) + gpiod_set_value(apb->clk_en, 0); if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) regulator_disable(apb->vcore); @@ -216,7 +212,7 @@ static void poweroff_seq(struct platform_device *pdev) regulator_disable(apb->vio); /* As part of exit, put APB back in reset state */ - assert_reset(apb->resetn_gpio); + assert_reset(apb->resetn); apb->state = ARCHE_PLATFORM_STATE_OFF; /* TODO: May have to send an event to SVC about this exit */ @@ -226,7 +222,7 @@ static void apb_bootret_deassert(struct device *dev) { struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); - gpio_set_value(apb->boot_ret_gpio, 0); + gpiod_set_value(apb->boot_ret, 0); } int apb_ctrl_coldboot(struct device *dev) @@ -322,66 +318,44 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, struct arche_apb_ctrl_drvdata *apb) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; int ret; - apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (apb->resetn_gpio < 0) { - dev_err(dev, "failed to get reset gpio\n"); - return apb->resetn_gpio; - } - ret = devm_gpio_request_one(dev, apb->resetn_gpio, - GPIOF_OUT_INIT_LOW, "apb-reset"); - if (ret) { - dev_err(dev, "Failed requesting reset gpio %d\n", - apb->resetn_gpio); + apb->resetn = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(apb->resetn)) { + ret = PTR_ERR(apb->resetn); + dev_err(dev, "Failed requesting reset GPIO: %d\n", ret); return ret; } - apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); - if (apb->boot_ret_gpio < 0) { - dev_err(dev, "failed to get boot retention gpio\n"); - return apb->boot_ret_gpio; - } - ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, - GPIOF_OUT_INIT_LOW, "boot retention"); - if (ret) { - dev_err(dev, "Failed requesting bootret gpio %d\n", - apb->boot_ret_gpio); + apb->boot_ret = devm_gpiod_get(dev, "boot-ret", GPIOD_OUT_LOW); + if (IS_ERR(apb->boot_ret)) { + ret = PTR_ERR(apb->boot_ret); + dev_err(dev, "Failed requesting bootret GPIO: %d\n", ret); return ret; } /* It's not mandatory to support power management interface */ - apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); - if (apb->pwroff_gpio < 0) { - dev_err(dev, "failed to get power off gpio\n"); - return apb->pwroff_gpio; - } - ret = devm_gpio_request_one(dev, apb->pwroff_gpio, - GPIOF_IN, "pwroff_n"); - if (ret) { - dev_err(dev, "Failed requesting pwroff_n gpio %d\n", - apb->pwroff_gpio); + apb->pwroff = devm_gpiod_get_optional(dev, "pwr-off", GPIOD_IN); + if (IS_ERR(apb->pwroff)) { + ret = PTR_ERR(apb->pwroff); + dev_err(dev, "Failed requesting pwroff_n GPIO: %d\n", ret); return ret; } /* Do not make clock mandatory as of now (for DB3) */ - apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); - if (apb->clk_en_gpio < 0) { - dev_warn(dev, "failed to get clock en gpio\n"); - } else if (gpio_is_valid(apb->clk_en_gpio)) { - ret = devm_gpio_request_one(dev, apb->clk_en_gpio, - GPIOF_OUT_INIT_LOW, "apb_clk_en"); - if (ret) { - dev_warn(dev, "Failed requesting APB clock en gpio %d\n", - apb->clk_en_gpio); - return ret; - } + apb->clk_en = devm_gpiod_get_optional(dev, "clock-en", GPIOD_OUT_LOW); + if (IS_ERR(apb->clk_en)) { + ret = PTR_ERR(apb->clk_en); + dev_err(dev, "Failed requesting APB clock en GPIO: %d\n", ret); + return ret; } - apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); - if (apb->pwrdn_gpio < 0) - dev_warn(dev, "failed to get power down gpio\n"); + apb->pwrdn = devm_gpiod_get(dev, "pwr-down", GPIOD_OUT_LOW); + if (IS_ERR(apb->pwrdn)) { + ret = PTR_ERR(apb->pwrdn); + dev_warn(dev, "Failed requesting power down GPIO: %d\n", ret); + return ret; + } /* Regulators are optional, as we may have fixed supply coming in */ apb->vcore = devm_regulator_get(dev, "vcore"); @@ -404,12 +378,8 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, } /* Only applicable for platform >= V2 */ - apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0); - if (apb->spi_en_gpio >= 0) { - if (of_property_read_bool(pdev->dev.of_node, - "spi-en-active-high")) - apb->spi_en_polarity_high = true; - } + if (of_property_read_bool(pdev->dev.of_node, "gb,spi-en-active-high")) + apb->spi_en_polarity_high = true; return 0; } diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c @@ -8,10 +8,9 @@ #include <linux/clk.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> @@ -45,14 +44,14 @@ enum svc_wakedetect_state { struct arche_platform_drvdata { /* Control GPIO signals to and from AP <=> SVC */ - int svc_reset_gpio; + struct gpio_desc *svc_reset; bool is_reset_act_hi; - int svc_sysboot_gpio; - int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ + struct gpio_desc *svc_sysboot; + struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ enum arche_platform_state state; - int svc_refclk_req; + struct gpio_desc *svc_refclk_req; struct clk *svc_ref_clk; struct pinctrl *pinctrl; @@ -85,9 +84,9 @@ static void arche_platform_set_wake_detect_state( arche_pdata->wake_detect_state = state; } -static inline void svc_reset_onoff(unsigned int gpio, bool onoff) +static inline void svc_reset_onoff(struct gpio_desc *gpio, bool onoff) { - gpio_set_value(gpio, onoff); + gpiod_set_raw_value(gpio, onoff); } static int apb_cold_boot(struct device *dev, void *data) @@ -116,7 +115,6 @@ static int apb_poweroff(struct device *dev, void *data) static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) { /* Enable interrupt here, to read event back from SVC */ - gpio_direction_input(arche_pdata->wake_detect_gpio); enable_irq(arche_pdata->wake_detect_irq); } @@ -160,7 +158,7 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) spin_lock_irqsave(&arche_pdata->wake_lock, flags); - if (gpio_get_value(arche_pdata->wake_detect_gpio)) { + if (gpiod_get_value(arche_pdata->wake_detect)) { /* wake/detect rising */ /* @@ -224,10 +222,9 @@ arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) dev_info(arche_pdata->dev, "Booting from cold boot state\n"); - svc_reset_onoff(arche_pdata->svc_reset_gpio, - arche_pdata->is_reset_act_hi); + svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); - gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); + gpiod_set_value(arche_pdata->svc_sysboot, 0); usleep_range(100, 200); ret = clk_prepare_enable(arche_pdata->svc_ref_clk); @@ -238,8 +235,7 @@ arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) } /* bring SVC out of reset */ - svc_reset_onoff(arche_pdata->svc_reset_gpio, - !arche_pdata->is_reset_act_hi); + svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi); arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); @@ -259,10 +255,9 @@ arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); - svc_reset_onoff(arche_pdata->svc_reset_gpio, - arche_pdata->is_reset_act_hi); + svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); - gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); + gpiod_set_value(arche_pdata->svc_sysboot, 1); usleep_range(100, 200); @@ -273,8 +268,7 @@ arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) return ret; } - svc_reset_onoff(arche_pdata->svc_reset_gpio, - !arche_pdata->is_reset_act_hi); + svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi); arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); @@ -305,8 +299,7 @@ arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) clk_disable_unprepare(arche_pdata->svc_ref_clk); /* As part of exit, put APB back in reset state */ - svc_reset_onoff(arche_pdata->svc_reset_gpio, - arche_pdata->is_reset_act_hi); + svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); } @@ -435,6 +428,7 @@ static int arche_platform_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int ret; + unsigned int flags; arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); @@ -444,61 +438,33 @@ static int arche_platform_probe(struct platform_device *pdev) /* setup svc reset gpio */ arche_pdata->is_reset_act_hi = of_property_read_bool(np, "svc,reset-active-high"); - arche_pdata->svc_reset_gpio = of_get_named_gpio(np, - "svc,reset-gpio", - 0); - if (!gpio_is_valid(arche_pdata->svc_reset_gpio)) { - dev_err(dev, "failed to get reset-gpio\n"); - return arche_pdata->svc_reset_gpio; - } - ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); - if (ret) { - dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); - return ret; - } - ret = gpio_direction_output(arche_pdata->svc_reset_gpio, - arche_pdata->is_reset_act_hi); - if (ret) { - dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + if (arche_pdata->is_reset_act_hi) + flags = GPIOD_OUT_HIGH; + else + flags = GPIOD_OUT_LOW; + + arche_pdata->svc_reset = devm_gpiod_get(dev, "svc,reset", flags); + if (IS_ERR(arche_pdata->svc_reset)) { + ret = PTR_ERR(arche_pdata->svc_reset); + dev_err(dev, "failed to request svc-reset GPIO: %d\n", ret); return ret; } arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); - arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, - "svc,sysboot-gpio", - 0); - if (!gpio_is_valid(arche_pdata->svc_sysboot_gpio)) { - dev_err(dev, "failed to get sysboot gpio\n"); - return arche_pdata->svc_sysboot_gpio; - } - ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); - if (ret) { - dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); - return ret; - } - ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); - if (ret) { - dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + arche_pdata->svc_sysboot = devm_gpiod_get(dev, "svc,sysboot", + GPIOD_OUT_LOW); + if (IS_ERR(arche_pdata->svc_sysboot)) { + ret = PTR_ERR(arche_pdata->svc_sysboot); + dev_err(dev, "failed to request sysboot0 GPIO: %d\n", ret); return ret; } /* setup the clock request gpio first */ - arche_pdata->svc_refclk_req = of_get_named_gpio(np, - "svc,refclk-req-gpio", - 0); - if (!gpio_is_valid(arche_pdata->svc_refclk_req)) { - dev_err(dev, "failed to get svc clock-req gpio\n"); - return arche_pdata->svc_refclk_req; - } - ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, - "svc-clk-req"); - if (ret) { - dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); - return ret; - } - ret = gpio_direction_input(arche_pdata->svc_refclk_req); - if (ret) { - dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); + arche_pdata->svc_refclk_req = devm_gpiod_get(dev, "svc,refclk-req", + GPIOD_IN); + if (IS_ERR(arche_pdata->svc_refclk_req)) { + ret = PTR_ERR(arche_pdata->svc_refclk_req); + dev_err(dev, "failed to request svc-clk-req GPIO: %d\n", ret); return ret; } @@ -515,19 +481,11 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); - arche_pdata->wake_detect_gpio = of_get_named_gpio(np, - "svc,wake-detect-gpio", - 0); - if (arche_pdata->wake_detect_gpio < 0) { - dev_err(dev, "failed to get wake detect gpio\n"); - return arche_pdata->wake_detect_gpio; - } - - ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, - "wake detect"); - if (ret) { - dev_err(dev, "Failed requesting wake_detect gpio %d\n", - arche_pdata->wake_detect_gpio); + arche_pdata->wake_detect = devm_gpiod_get(dev, "svc,wake-detect", + GPIOD_IN); + if (IS_ERR(arche_pdata->wake_detect)) { + ret = PTR_ERR(arche_pdata->wake_detect); + dev_err(dev, "Failed requesting wake_detect GPIO: %d\n", ret); return ret; } @@ -538,7 +496,7 @@ static int arche_platform_probe(struct platform_device *pdev) spin_lock_init(&arche_pdata->wake_lock); mutex_init(&arche_pdata->platform_state_mutex); arche_pdata->wake_detect_irq = - gpio_to_irq(arche_pdata->wake_detect_gpio); + gpiod_to_irq(arche_pdata->wake_detect); ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, arche_platform_wd_irq, diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c @@ -923,7 +923,6 @@ static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb, break; default: return -EINVAL; - } dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name, diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c @@ -65,7 +65,7 @@ static struct attribute *bundle_attrs[] = { ATTRIBUTE_GROUPS(bundle); static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, - u8 bundle_id) + u8 bundle_id) { struct gb_bundle *bundle; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h @@ -88,7 +88,7 @@ void gb_connection_mode_switch_prepare(struct gb_connection *connection); void gb_connection_mode_switch_complete(struct gb_connection *connection); void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, - u8 *data, size_t length); + u8 *data, size_t length); void gb_connection_latency_tag_enable(struct gb_connection *connection); void gb_connection_latency_tag_disable(struct gb_connection *connection); diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c @@ -15,7 +15,6 @@ #define GB_CONTROL_VERSION_MAJOR 0 #define GB_CONTROL_VERSION_MINOR 1 - static int gb_control_get_version(struct gb_control *control) { struct gb_interface *intf = control->connection->intf; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c @@ -266,7 +266,7 @@ static int greybus_remove(struct device *dev) } int greybus_register_driver(struct greybus_driver *driver, struct module *owner, - const char *mod_name) + const char *mod_name) { int retval; diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c @@ -9,9 +9,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/gpio/driver.h> #include <linux/mutex.h> #include "greybus.h" @@ -39,11 +39,6 @@ struct gb_gpio_controller { struct gpio_chip chip; struct irq_chip irqc; - struct irq_chip *irqchip; - struct irq_domain *irqdomain; - unsigned int irq_base; - irq_flow_handler_t irq_handler; - unsigned int irq_default_type; struct mutex irq_lock; }; #define gpio_chip_to_gb_gpio_controller(chip) \ @@ -391,7 +386,7 @@ static int gb_gpio_request_handler(struct gb_operation *op) return -EINVAL; } - irq = irq_find_mapping(ggc->irqdomain, event->which); + irq = irq_find_mapping(ggc->chip.irq.domain, event->which); if (!irq) { dev_err(dev, "failed to find IRQ\n"); return -EINVAL; @@ -506,135 +501,6 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) return ret; } -/** - * gb_gpio_irq_map() - maps an IRQ into a GB gpio irqchip - * @d: the irqdomain used by this irqchip - * @irq: the global irq number used by this GB gpio irqchip irq - * @hwirq: the local IRQ/GPIO line offset on this GB gpio - * - * This function will set up the mapping for a certain IRQ line on a - * GB gpio by assigning the GB gpio as chip data, and using the irqchip - * stored inside the GB gpio. - */ -static int gb_gpio_irq_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct gpio_chip *chip = domain->host_data; - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - - irq_set_chip_data(irq, ggc); - irq_set_chip_and_handler(irq, ggc->irqchip, ggc->irq_handler); - irq_set_noprobe(irq); - /* - * No set-up of the hardware will happen if IRQ_TYPE_NONE - * is passed as default type. - */ - if (ggc->irq_default_type != IRQ_TYPE_NONE) - irq_set_irq_type(irq, ggc->irq_default_type); - - return 0; -} - -static void gb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) -{ - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); -} - -static const struct irq_domain_ops gb_gpio_domain_ops = { - .map = gb_gpio_irq_map, - .unmap = gb_gpio_irq_unmap, -}; - -/** - * gb_gpio_irqchip_remove() - removes an irqchip added to a gb_gpio_controller - * @ggc: the gb_gpio_controller to remove the irqchip from - * - * This is called only from gb_gpio_remove() - */ -static void gb_gpio_irqchip_remove(struct gb_gpio_controller *ggc) -{ - unsigned int offset; - - /* Remove all IRQ mappings and delete the domain */ - if (ggc->irqdomain) { - for (offset = 0; offset < (ggc->line_max + 1); offset++) - irq_dispose_mapping(irq_find_mapping(ggc->irqdomain, - offset)); - irq_domain_remove(ggc->irqdomain); - } - - if (ggc->irqchip) - ggc->irqchip = NULL; -} - -/** - * gb_gpio_irqchip_add() - adds an irqchip to a gpio chip - * @chip: the gpio chip to add the irqchip to - * @irqchip: the irqchip to add to the adapter - * @first_irq: if not dynamically assigned, the base (first) IRQ to - * allocate gpio irqs from - * @handler: the irq handler to use (often a predefined irq core function) - * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE - * to have the core avoid setting up any default type in the hardware. - * - * This function closely associates a certain irqchip with a certain - * gpio chip, providing an irq domain to translate the local IRQs to - * global irqs, and making sure that the gpio chip - * is passed as chip data to all related functions. Driver callbacks - * need to use container_of() to get their local state containers back - * from the gpio chip passed as chip data. An irqdomain will be stored - * in the gpio chip that shall be used by the driver to handle IRQ number - * translation. The gpio chip will need to be initialized and registered - * before calling this function. - */ -static int gb_gpio_irqchip_add(struct gpio_chip *chip, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type) -{ - struct gb_gpio_controller *ggc; - unsigned int offset; - unsigned int irq_base; - - if (!chip || !irqchip) - return -EINVAL; - - ggc = gpio_chip_to_gb_gpio_controller(chip); - - ggc->irqchip = irqchip; - ggc->irq_handler = handler; - ggc->irq_default_type = type; - ggc->irqdomain = irq_domain_add_simple(NULL, - ggc->line_max + 1, first_irq, - &gb_gpio_domain_ops, chip); - if (!ggc->irqdomain) { - ggc->irqchip = NULL; - return -EINVAL; - } - - /* - * Prepare the mapping since the irqchip shall be orthogonal to - * any gpio calls. If the first_irq was zero, this is - * necessary to allocate descriptors for all IRQs. - */ - for (offset = 0; offset < (ggc->line_max + 1); offset++) { - irq_base = irq_create_mapping(ggc->irqdomain, offset); - if (offset == 0) - ggc->irq_base = irq_base; - } - - return 0; -} - -static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - - return irq_find_mapping(ggc->irqdomain, offset); -} - static int gb_gpio_probe(struct gbphy_device *gbphy_dev, const struct gbphy_device_id *id) { @@ -694,7 +560,6 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, gpio->get = gb_gpio_get; gpio->set = gb_gpio_set; gpio->set_config = gb_gpio_set_config; - gpio->to_irq = gb_gpio_to_irq; gpio->base = -1; /* Allocate base dynamically */ gpio->ngpio = ggc->line_max + 1; gpio->can_sleep = true; @@ -703,24 +568,24 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_line_free; - ret = gb_gpio_irqchip_add(gpio, irqc, 0, - handle_level_irq, IRQ_TYPE_NONE); + ret = gpiochip_add(gpio); if (ret) { - dev_err(&gbphy_dev->dev, "failed to add irq chip: %d\n", ret); + dev_err(&gbphy_dev->dev, "failed to add gpio chip: %d\n", ret); goto exit_line_free; } - ret = gpiochip_add(gpio); + ret = gpiochip_irqchip_add(gpio, irqc, 0, handle_level_irq, + IRQ_TYPE_NONE); if (ret) { - dev_err(&gbphy_dev->dev, "failed to add gpio chip: %d\n", ret); - goto exit_gpio_irqchip_remove; + dev_err(&gbphy_dev->dev, "failed to add irq chip: %d\n", ret); + goto exit_gpiochip_remove; } gbphy_runtime_put_autosuspend(gbphy_dev); return 0; -exit_gpio_irqchip_remove: - gb_gpio_irqchip_remove(ggc); +exit_gpiochip_remove: + gpiochip_remove(gpio); exit_line_free: kfree(ggc->lines); exit_connection_disable: @@ -744,7 +609,6 @@ static void gb_gpio_remove(struct gbphy_device *gbphy_dev) gb_connection_disable_rx(connection); gpiochip_remove(&ggc->chip); - gb_gpio_irqchip_remove(ggc); gb_connection_disable(connection); gb_connection_destroy(connection); kfree(ggc->lines); diff --git a/drivers/staging/gs_fpgaboot/gs_fpgaboot.c b/drivers/staging/gs_fpgaboot/gs_fpgaboot.c @@ -1,14 +1,4 @@ -/* - * 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. - */ +// SPDX-License-Identifier: GPL-2.0+ #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/staging/gs_fpgaboot/gs_fpgaboot.h b/drivers/staging/gs_fpgaboot/gs_fpgaboot.h @@ -1,14 +1,4 @@ -/* - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0+ */ #include <linux/firmware.h> diff --git a/drivers/staging/gs_fpgaboot/io.c b/drivers/staging/gs_fpgaboot/io.c @@ -1,15 +1,4 @@ -/* - * 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. - */ +// SPDX-License-Identifier: GPL-2.0+ #include <linux/kernel.h> #include <linux/init.h> @@ -50,8 +39,7 @@ int xl_supported_prog_bus_width(enum wbus bus_bytes) case bus_2byte: break; default: - pr_err("unsupported program bus width %d\n", - bus_bytes); + pr_err("unsupported program bus width %d\n", bus_bytes); return 0; } diff --git a/drivers/staging/gs_fpgaboot/io.h b/drivers/staging/gs_fpgaboot/io.h @@ -1,14 +1,4 @@ -/* - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0+ */ #define GPDIR 0 #define GPCFG 4 /* open drain or not */ diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig @@ -3,40 +3,6 @@ # menu "Analog to digital converters" -config AD7606 - tristate "Analog Devices AD7606 ADC driver" - depends on GPIOLIB || COMPILE_TEST - depends on HAS_IOMEM - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for Analog Devices: - ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). - - To compile this driver as a module, choose M here: the - module will be called ad7606. - -config AD7606_IFACE_PARALLEL - tristate "parallel interface support" - depends on AD7606 - help - Say yes here to include parallel interface support on the AD7606 - ADC driver. - - To compile this driver as a module, choose M here: the - module will be called ad7606_parallel. - -config AD7606_IFACE_SPI - tristate "spi interface support" - depends on AD7606 - depends on SPI - help - Say yes here to include parallel interface support on the AD7606 - ADC driver. - - To compile this driver as a module, choose M here: the - module will be called ad7606_spi. - config AD7780 tristate "Analog Devices AD7780 and similar ADCs driver" depends on SPI diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile @@ -3,10 +3,6 @@ # Makefile for industrial I/O ADC drivers # -obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o -obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o -obj-$(CONFIG_AD7606) += ad7606.o - obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7816) += ad7816.o obj-$(CONFIG_AD7192) += ad7192.o diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c @@ -97,6 +97,10 @@ #define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \ AD7280A_CELL_VOLTAGE_1 + 1) +#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) ((d * AD7280A_CELLS_PER_DEV) + c) +#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) ((d * AD7280A_CELLS_PER_DEV) + \ + c - AD7280A_CELLS_PER_DEV) + #define AD7280A_DEVADDR_MASTER 0 #define AD7280A_DEVADDR_ALL 0x1F /* 5-bit device address is sent LSB first */ @@ -496,72 +500,171 @@ static const struct attribute_group ad7280_attrs_group = { .attrs = ad7280_attributes, }; +static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = i; + chan->channel2 = chan->channel + 1; +} + +static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i) +{ + chan->type = IIO_TEMP; + chan->channel = i; +} + +static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr, + int cnt) +{ + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->address = addr; + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 32; +} + +static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan, + int cnt, int dev) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = 0; + chan->channel2 = dev * AD7280A_CELLS_PER_DEV; + chan->address = AD7280A_ALL_CELLS; + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 32; + chan->scan_type.storagebits = 32; +} + +static void ad7280_timestamp_channel_init(struct iio_chan_spec *chan, int cnt) +{ + chan->type = IIO_TIMESTAMP; + chan->channel = -1; + chan->scan_index = cnt; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = 64; + chan->scan_type.storagebits = 64; +} + +static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt) +{ + int addr, ch, i; + struct iio_chan_spec *chan; + + for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++) { + chan = &st->channels[*cnt]; + + if (ch < AD7280A_AUX_ADC_1) { + i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch); + ad7280_voltage_channel_init(chan, i); + } else { + i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch); + ad7280_temp_channel_init(chan, i); + } + + addr = ad7280a_devaddr(dev) << 8 | ch; + ad7280_common_fields_init(chan, addr, *cnt); + + (*cnt)++; + } +} + static int ad7280_channel_init(struct ad7280_state *st) { - int dev, ch, cnt; + int dev, cnt = 0; st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 2, sizeo