1st set of new IIO/counter device support, features and cleanup for 5.14

There are a couple of large cleanup sets in here alongside a number of new
 drivers.
 
 Note an immutable branch merged to add a stub for i2c_verify_client()
 as needed to avoid a build issue in the fxls8962af driver as a result of a
 workaround for a device errata that only applies when i2c interface is used.
 
 Counters
 ========
 
 New device support
 * intel,quadrature encoder peripheral found on Elkhart Lake platforms.
   - New driver.
 
 IIO
 ===
 
 New device support
 * amstaos,tsl2591 ambient light sensor.
   - New driver + bindings
   - Follow up fix to ensure some local variables are suitable for error
     handling.
 * fsl,fxls8962af + fsl,fxls8964af accelerometers
   - New driver + bindings
   - Includes an errata work around that cause a build bot failure fixed
     by adding a stub to i2c.
 * kionix,kxcjk-1013
   - Add support for KX023-1025 device.  Mostly a different register map
     that needed to be supported.
 * murata,sca3300 accelerometer
   - New driver + bindings
 * st,lsm9ds0 IMU
   - Rework of st,sensors driver to cleanly support this new glue driver
     that enables the two parts of the LSM9DS0.
 * ti,tsc2046 touchscreen controller ADC.
   - New driver. Intent here is to use this with existing IIO consumer
     drivers such as resistive-adc-touch.
   - Follow up fix to avoid an issue with unsigned subtraction in error
     check.
 * ti,tmp117 digital temperature sensor
   - New driver + bindings
 
 Features
 * adi,ad5755
   - Add missing dt-binding doc
 * adi,ad7298
   - Add ACPI ID used on Intel Galileo Gen 1 boards.
   - Add missing dt-binding doc
 * adi,ad7476
   - Add missing dt-binding doc
 * adi,ad7746
   - Add missing dt-binding doc for this driver that will hopefully move out
     of staging shortly. Update staging driver to use the binding instead of
     platform data.
 * adi,adis16201 + adis16209
   - Add missing dt-binding doc
 * adi,adis16480
   - Support burst mode for adis16495 and adis16497 parts.
 * bosch,bma220
   - Add missing dt-binding doc
 * fsl,mma7455
   - Add missing dt-binding doc
 * iio-rescale
   - Support handling of processed channels from provider.  Some ADCs
     require (typically non linear) calibration functions to be applied,
     and so provide only IIO_CHAN_INFO_PROCESSED read back. They can be
     used as providers to the iio-rescale driver which needs to handle them
     somewhat differently from IIO_CHAN_INFO_RAW
 * sensiron,sps30
   - Support the serial interface.  Note this required significant
     refactoring of existing driver.
 * st,st-sensors
   - Add mount matrix support for normal dt-binding whilst continuing to
     support the odd ACPI approach for accelerometers.
 * ti,dac082s085 + similar
   - Add missing dt-binding doc
 * trivial-devices - add entries for
   - memsic,mx4005, memsic,mx6255 and memsic,mxc6655
   - sensortek,stk8312 and sensortek,stk8ba50
 
 Cleanup / minor fixes
 * core
   - Use devm_add_action_or_reset() to replace boilerplate in several
     driver and core IIO devm_* functions.
   - Fix an error path issue introduced by above, that could return an
     error pointer rather than the expected null from dev_iio_device_alloc()
   - Move more IIO internals related fields from struct iio_dev to
     struct iio_dev_opaque.
   - Drop unused final update of in_loc in demux setup.
 * Docs
   - Move some docs from driver specific to core dos to avoid replication
     of names which the documentation builder does not allow.
     Note this means adding a few device specific notes to the general docs
     to cover the more unusual uses of the ABI.
   - ABI: Move old buffer/* and scan_elements/* docs to obsolete as now we
     have the bufferX/* variant.  Not we are not getting rid of these
     interfaces, just encouraging new code to use the new interface.
 * IIO wide:
   - Tidy up new cases of dev.parent etc being set in drivers as the core
     now does it.
   - Fix more cases of possible miss-aligned buffers when passed to
     iio_push_to_buffers_with_timestamp().  Note we only have one known
     instance of anyone seeing this bug actually happening, so this has been
     a low priority cleanup effort for several cycles.
   - sysfs_emit() used in more drivers.
   - Runtime pm tidy up and use of pm_runtime_resume_and_get()
   - Buffer alignment fixes as iio_push_to_buffers_with_timestamp requires
     that the timestamp when inserted by naturally aligned + consumers can
     assume that all fields are naturally aligned. Part of a long running
     effort, with at least 2 more series to come tackling additional
     variants.
   - Stop specifying "mount-matrix" property name in every lookup of the
     mount matrix from firmware by hard coding it in the core.
 * adi,ad7476
   - Handle the variety of different regulators used by the parts supported
     by this driver (came up in dt-binding review)
 * adi,ad7746
   - Trivial drop of if (ret) return ret; return 0; pattern
   - Tidy up comments
   - Pull capdac setup out to own function.
 * adi,ad7766
   - Trivial drop of if (ret) return ret; return 0; pattern
 * adi,adis
   - Avoid returning error codes in interrupt handlers.
   - Handle a failure in spi_write in the trigger handler.
   - Rework to add updating of device page after changing it.
   - Don't push data to IIO buffers when read failed.
   - Add configuration of burst max speed to core avoid handling this in
     each driver.
   - Use the adis_dev_lock() helper in adis16260 and adis16136 drivers.
   - Excessive includes cleanup via include-what-you-use static checker
     after zero day highlighted that these needed updating.
 * afe
   - Amend binding to add #io-channel-cells, thus allowing this IIO
     consumer to also be an IIO provider.
 * aosong,am2315
   - Drop ACPI id. Unlikely this one is in the wild and it isn't valid
     ACPI naming.
 * bosch,bma180
   - Adding missing bandwidth settings (500, 1000 Hz)
 * bosch,bme680
   - Drop ACPI id. Unlikely this one is in the wild and it isn't valid
     ACPI naming.
 * ep93xx_adc,
   - Drop a redundant error print.
 * maxim,max118
   - Convert remainder of probe() to devm_ managed functions.
   - Avoid some repeated jumping back and forth between iio_dev and
     spi structures.
 * maxim,max11100
   - Use get_unaligned_be16() instead of open coding.
   - Convert remainder of probe() to devm_ managed functions.
 * samsung,exynos_adc
   - Unused error value dropped.
 * sensiron,sgp30
   - Drop use of %hx in favor of %x and letting the normal type conversion
     work.
 * sensortek,stk8312
   - Add lowercase device id and note uppercase version deprecated.
   - Drop ACPI id. Unlikely this one is in the wild and it isn't valid
     ACPI naming.
 * sprx,sc72xx_adc
   - add MODULE_DEVICE_TABLE
 * st,lsm6dsx
   - Fix docs of valid ODRs
 * st,sensors
   - dt-binding rework.  Two efforts on this crossed in a previous cycle
     so this update cherry picks the best of the two yaml conversions.
   - Don't copy the channel spec array as now ext_info is no longer modified.
 * st,stm32-adc
   - tidy up some docs that were marked as kernel-doc but aren't.
 * ti,adc081c, ti,adc0832, ti,adc108s102 and ti,adc161s626
   - Convert remainder of probe() functions to devm_ managed functions
     to simplify error handing and remove paths.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmC/mCoRHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0FoidAQ//SqpbBeEy8HATSHccooHwHI3eK+hnj0n9
 9zr6/7o52EQ0lFN6V7OLp0XNL3DNIV8oYAyvzYZ4Qh2NXLYQHDnqiiUyLxCfctqu
 Ii+9NmVmuk/PlPRRubQRZE+Czdtwgsp7dRQOYJiuxUeKVD/EUVjl1FmpsiPtGeaa
 iU6JaYtdF3ie0b1zQCwQTYYsM8lZ2/ovKW8F29K5ALnrDd9h6Ad0p5QDvyDxyajp
 VyLRJa7nwAfK5rP6efuNsDfzbMycTPtHkcC+Pgec/2RrXL5mDz4EXHI1nOUZAGdb
 UaN/uDpytAxJZk6Fs2f+RdgevlQgpBxAXGDHE2YHkcZi7X0ppWOjeIZFSDbDiaHO
 XlSQgOelUqKtHhRZ3MYHxbSOgO3Vif6ecCDMNCN78E0YE3kQHHSwY0JMGgUeHIGG
 hQPKGaD1AKzh7AsbPbazYW6VX4dDDWcr8pQ8D9wWLUKikcZLKqRH5uAwvjZ+NjuC
 Bfnjx/QhmIhbs0gFaw4Q5mvYQ3Zmfh7nzcW98jwcbR6pOqKvIeqzw9OARRHaimrd
 /GRCiccxKtU8J7f5l+MSzYQt4hT0Ef1vuq9Ak5SDCr3Fwnix5ipFVLkipWvgJ7JD
 OqubcwwW5EfgZPY/X7nsK/U6v8SlqF4XrvCVky4MUt0x1YXxc/tjYak8oLEqpMVC
 gQP3KUZIYeA=
 =Zved
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-5.14a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

1st set of new IIO/counter device support, features and cleanup for 5.14

There are a couple of large cleanup sets in here alongside a number of new
drivers.

Note an immutable branch merged to add a stub for i2c_verify_client()
as needed to avoid a build issue in the fxls8962af driver as a result of a
workaround for a device errata that only applies when i2c interface is used.

Counters
========

New device support
* intel,quadrature encoder peripheral found on Elkhart Lake platforms.
  - New driver.

IIO
===

New device support
* amstaos,tsl2591 ambient light sensor.
  - New driver + bindings
  - Follow up fix to ensure some local variables are suitable for error
    handling.
* fsl,fxls8962af + fsl,fxls8964af accelerometers
  - New driver + bindings
  - Includes an errata work around that cause a build bot failure fixed
    by adding a stub to i2c.
* kionix,kxcjk-1013
  - Add support for KX023-1025 device.  Mostly a different register map
    that needed to be supported.
* murata,sca3300 accelerometer
  - New driver + bindings
* st,lsm9ds0 IMU
  - Rework of st,sensors driver to cleanly support this new glue driver
    that enables the two parts of the LSM9DS0.
* ti,tsc2046 touchscreen controller ADC.
  - New driver. Intent here is to use this with existing IIO consumer
    drivers such as resistive-adc-touch.
  - Follow up fix to avoid an issue with unsigned subtraction in error
    check.
* ti,tmp117 digital temperature sensor
  - New driver + bindings

Features
* adi,ad5755
  - Add missing dt-binding doc
* adi,ad7298
  - Add ACPI ID used on Intel Galileo Gen 1 boards.
  - Add missing dt-binding doc
* adi,ad7476
  - Add missing dt-binding doc
* adi,ad7746
  - Add missing dt-binding doc for this driver that will hopefully move out
    of staging shortly. Update staging driver to use the binding instead of
    platform data.
* adi,adis16201 + adis16209
  - Add missing dt-binding doc
* adi,adis16480
  - Support burst mode for adis16495 and adis16497 parts.
* bosch,bma220
  - Add missing dt-binding doc
* fsl,mma7455
  - Add missing dt-binding doc
* iio-rescale
  - Support handling of processed channels from provider.  Some ADCs
    require (typically non linear) calibration functions to be applied,
    and so provide only IIO_CHAN_INFO_PROCESSED read back. They can be
    used as providers to the iio-rescale driver which needs to handle them
    somewhat differently from IIO_CHAN_INFO_RAW
* sensiron,sps30
  - Support the serial interface.  Note this required significant
    refactoring of existing driver.
* st,st-sensors
  - Add mount matrix support for normal dt-binding whilst continuing to
    support the odd ACPI approach for accelerometers.
* ti,dac082s085 + similar
  - Add missing dt-binding doc
* trivial-devices - add entries for
  - memsic,mx4005, memsic,mx6255 and memsic,mxc6655
  - sensortek,stk8312 and sensortek,stk8ba50

Cleanup / minor fixes
* core
  - Use devm_add_action_or_reset() to replace boilerplate in several
    driver and core IIO devm_* functions.
  - Fix an error path issue introduced by above, that could return an
    error pointer rather than the expected null from dev_iio_device_alloc()
  - Move more IIO internals related fields from struct iio_dev to
    struct iio_dev_opaque.
  - Drop unused final update of in_loc in demux setup.
* Docs
  - Move some docs from driver specific to core dos to avoid replication
    of names which the documentation builder does not allow.
    Note this means adding a few device specific notes to the general docs
    to cover the more unusual uses of the ABI.
  - ABI: Move old buffer/* and scan_elements/* docs to obsolete as now we
    have the bufferX/* variant.  Not we are not getting rid of these
    interfaces, just encouraging new code to use the new interface.
* IIO wide:
  - Tidy up new cases of dev.parent etc being set in drivers as the core
    now does it.
  - Fix more cases of possible miss-aligned buffers when passed to
    iio_push_to_buffers_with_timestamp().  Note we only have one known
    instance of anyone seeing this bug actually happening, so this has been
    a low priority cleanup effort for several cycles.
  - sysfs_emit() used in more drivers.
  - Runtime pm tidy up and use of pm_runtime_resume_and_get()
  - Buffer alignment fixes as iio_push_to_buffers_with_timestamp requires
    that the timestamp when inserted by naturally aligned + consumers can
    assume that all fields are naturally aligned. Part of a long running
    effort, with at least 2 more series to come tackling additional
    variants.
  - Stop specifying "mount-matrix" property name in every lookup of the
    mount matrix from firmware by hard coding it in the core.
* adi,ad7476
  - Handle the variety of different regulators used by the parts supported
    by this driver (came up in dt-binding review)
* adi,ad7746
  - Trivial drop of if (ret) return ret; return 0; pattern
  - Tidy up comments
  - Pull capdac setup out to own function.
* adi,ad7766
  - Trivial drop of if (ret) return ret; return 0; pattern
* adi,adis
  - Avoid returning error codes in interrupt handlers.
  - Handle a failure in spi_write in the trigger handler.
  - Rework to add updating of device page after changing it.
  - Don't push data to IIO buffers when read failed.
  - Add configuration of burst max speed to core avoid handling this in
    each driver.
  - Use the adis_dev_lock() helper in adis16260 and adis16136 drivers.
  - Excessive includes cleanup via include-what-you-use static checker
    after zero day highlighted that these needed updating.
* afe
  - Amend binding to add #io-channel-cells, thus allowing this IIO
    consumer to also be an IIO provider.
* aosong,am2315
  - Drop ACPI id. Unlikely this one is in the wild and it isn't valid
    ACPI naming.
* bosch,bma180
  - Adding missing bandwidth settings (500, 1000 Hz)
* bosch,bme680
  - Drop ACPI id. Unlikely this one is in the wild and it isn't valid
    ACPI naming.
* ep93xx_adc,
  - Drop a redundant error print.
* maxim,max118
  - Convert remainder of probe() to devm_ managed functions.
  - Avoid some repeated jumping back and forth between iio_dev and
    spi structures.
* maxim,max11100
  - Use get_unaligned_be16() instead of open coding.
  - Convert remainder of probe() to devm_ managed functions.
* samsung,exynos_adc
  - Unused error value dropped.
* sensiron,sgp30
  - Drop use of %hx in favor of %x and letting the normal type conversion
    work.
* sensortek,stk8312
  - Add lowercase device id and note uppercase version deprecated.
  - Drop ACPI id. Unlikely this one is in the wild and it isn't valid
    ACPI naming.
* sprx,sc72xx_adc
  - add MODULE_DEVICE_TABLE
* st,lsm6dsx
  - Fix docs of valid ODRs
* st,sensors
  - dt-binding rework.  Two efforts on this crossed in a previous cycle
    so this update cherry picks the best of the two yaml conversions.
  - Don't copy the channel spec array as now ext_info is no longer modified.
* st,stm32-adc
  - tidy up some docs that were marked as kernel-doc but aren't.
* ti,adc081c, ti,adc0832, ti,adc108s102 and ti,adc161s626
  - Convert remainder of probe() functions to devm_ managed functions
    to simplify error handing and remove paths.

* tag 'iio-for-5.14a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (171 commits)
  i2c: core: Add stub for i2c_verify_client() if !CONFIG_I2C
  iio: adis: Cleanout unused headers
  iio: accel: bma180: Add missing 500 Hz / 1000 Hz bandwidth
  counter: Add support for Intel Quadrature Encoder Peripheral
  staging: iio: cdc: ad7746: extract capac setup to own function
  staging: iio: cdc: ad7746: clean up probe return
  staging: iio: cdc: ad7746: remove ordinary comments
  iio: adc: ti-adc161s626: Use devm managed functions for all of probe.
  iio: adc: ti-adc108s102: Use devm managed functions for all of probe()
  iio: adc: ti-adc0832: Use devm managed functions for all of probe()
  iio: adc: ti-adc081c: Use devm managed functions for all of probe()
  iio: adc: max1118: Avoid jumping back and forth between spi and iio structures
  iio: adc: max1118: Use devm_ managed functions for all of probe
  iio: adc: max11100: Use devm_ functions for rest of probe()
  iio: adc: max11100: Use get_unaligned_be16() rather than opencoding.
  iio: chemical: sgp30: Drop use of %hx in format string.
  iio: gyro: st_gyro: Support mount matrix
  iio: magnetometer: st_magn: Support mount matrix
  iio: accel: st_sensors: Stop copying channels
  iio: accel: st_sensors: Support generic mounting matrix
  ...
This commit is contained in:
Greg Kroah-Hartman 2021-06-09 12:11:49 +02:00
commit 6771fb0b94
212 changed files with 8774 additions and 1816 deletions

View File

@ -0,0 +1,182 @@
What: /sys/bus/iio/devices/iio:deviceX/buffer/length
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
Number of scans contained by the buffer.
Since Kernel 5.11, multiple buffers are supported.
so, it is better to use, instead:
/sys/bus/iio/devices/iio:deviceX/bufferY/length
What: /sys/bus/iio/devices/iio:deviceX/buffer/enable
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
Actually start the buffer capture up. Will start trigger
if first device and appropriate.
Since Kernel 5.11, multiple buffers are supported.
so, it is better to use, instead:
/sys/bus/iio/devices/iio:deviceX/bufferY/enable
What: /sys/bus/iio/devices/iio:deviceX/scan_elements
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
Directory containing interfaces for elements that will be
captured for a single triggered sample set in the buffer.
Since kernel 5.11 the scan_elements attributes are merged into
the bufferY directory, to be configurable per buffer.
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_en
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_en
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_en
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_x_en
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_y_en
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_z_en
What: /sys/.../iio:deviceX/scan_elements/in_magn_x_en
What: /sys/.../iio:deviceX/scan_elements/in_magn_y_en
What: /sys/.../iio:deviceX/scan_elements/in_magn_z_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_tilt_comp_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_tilt_comp_en
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
What: /sys/.../iio:deviceX/scan_elements/in_pressure_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_en
What: /sys/.../iio:deviceX/scan_elements/in_proximity_en
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
Scan element control for triggered data capture.
Since kernel 5.11 the scan_elements attributes are merged into
the bufferY directory, to be configurable per buffer.
What: /sys/.../iio:deviceX/scan_elements/in_accel_type
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_type
What: /sys/.../iio:deviceX/scan_elements/in_magn_type
What: /sys/.../iio:deviceX/scan_elements/in_incli_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_type
What: /sys/.../iio:deviceX/scan_elements/in_proximity_type
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
Description of the scan element data storage within the buffer
and hence the form in which it is read from user-space.
Form is [be|le]:[s|u]bits/storagebits[>>shift].
be or le specifies big or little endian. s or u specifies if
signed (2's complement) or unsigned. bits is the number of bits
of data and storagebits is the space (after padding) that it
occupies in the buffer. shift if specified, is the shift that
needs to be applied prior to masking out unused bits. Some
devices put their data in the middle of the transferred elements
with additional information on both sides. Note that some
devices will have additional information in the unused bits
so to get a clean value, the bits value must be used to mask
the buffer output value appropriately. The storagebits value
also specifies the data alignment. So s48/64>>2 will be a
signed 48 bit integer stored in a 64 bit location aligned to
a 64 bit boundary. To obtain the clean value, shift right 2
and apply a mask to zero the top 16 bits of the result.
For other storage combinations this attribute will be extended
appropriately.
Since kernel 5.11 the scan_elements attributes are merged into
the bufferY directory, to be configurable per buffer.
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_x_index
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_y_index
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_z_index
What: /sys/.../iio:deviceX/scan_elements/in_magn_x_index
What: /sys/.../iio:deviceX/scan_elements/in_magn_y_index
What: /sys/.../iio:deviceX/scan_elements/in_magn_z_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_tilt_comp_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_tilt_comp_index
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_index
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_index
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_index
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_index
What: /sys/.../iio:deviceX/scan_elements/in_pressure_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_index
What: /sys/.../iio:deviceX/scan_elements/in_proximity_index
KernelVersion: 2.6.37
Description:
A single positive integer specifying the position of this
scan element in the buffer. Note these are not dependent on
what is enabled and may not be contiguous. Thus for user-space
to establish the full layout these must be used in conjunction
with all _en attributes to establish which channels are present,
and the relevant _type attributes to establish the data storage
format.
Since kernel 5.11 the scan_elements attributes are merged into
the bufferY directory, to be configurable per buffer.
What: /sys/bus/iio/devices/iio:deviceX/buffer/watermark
KernelVersion: 4.2
Contact: linux-iio@vger.kernel.org
Description:
A single positive integer specifying the maximum number of scan
elements to wait for.
Poll will block until the watermark is reached.
Blocking read will wait until the minimum between the requested
read amount or the low water mark is available.
Non-blocking read will retrieve the available samples from the
buffer even if there are less samples then watermark level. This
allows the application to block on poll with a timeout and read
the available samples after the timeout expires and thus have a
maximum delay guarantee.
Since Kernel 5.11, multiple buffers are supported.
so, it is better to use, instead:
/sys/bus/iio/devices/iio:deviceX/bufferY/watermark
What: /sys/bus/iio/devices/iio:deviceX/buffer/data_available
KernelVersion: 4.16
Contact: linux-iio@vger.kernel.org
Description:
A read-only value indicating the bytes of data available in the
buffer. In the case of an output buffer, this indicates the
amount of empty space available to write data to. In the case of
an input buffer, this indicates the amount of data available for
reading.
Since Kernel 5.11, multiple buffers are supported.
so, it is better to use, instead:
/sys/bus/iio/devices/iio:deviceX/bufferY/data_available

View File

@ -193,6 +193,15 @@ Description:
both edges: both edges:
Any state transition. Any state transition.
What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns
KernelVersion: 5.14
Contact: linux-iio@vger.kernel.org
Description:
If the counter device supports programmable spike filter this
attribute indicates the value in nanoseconds where noise pulses
shorter or equal to configured value are ignored. Value 0 means
filter is disabled.
What: /sys/bus/counter/devices/counterX/name What: /sys/bus/counter/devices/counterX/name
KernelVersion: 5.2 KernelVersion: 5.2
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org

View File

@ -455,6 +455,19 @@ Contact: linux-iio@vger.kernel.org
Description: Description:
Hardware applied calibration offset (assumed to fix production Hardware applied calibration offset (assumed to fix production
inaccuracies). inaccuracies).
icm42600: For this device values are real physical offsets
expressed in SI units (m/s^2 for accelerometers and rad/s
for gyroscope)/
What: /sys/bus/iio/devices/iio:deviceX/in_accel_calibbias_available
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_calibbias_available
KernelVersion: 5.8
Contact: linux-iio@vger.kernel.org
Description:
Available values of calibbias. Maybe expressed as either of:
- a small discrete set of values like "0 2 4 6 8"
- a range specified as "[min step max]"
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
@ -652,6 +665,25 @@ Description:
Output frequency for channel Y in Hz. The number must always be Output frequency for channel Y in Hz. The number must always be
specified and unique if the output corresponds to a single specified and unique if the output corresponds to a single
channel. channel.
Some drivers have additional constraints:
ADF4371 has an integrated VCO with fundamendal output
frequency ranging from 4000000000 Hz 8000000000 Hz.
out_altvoltage0_frequency:
A divide by 1, 2, 4, 8, 16, 32 or circuit generates
frequencies from 62500000 Hz to 8000000000 Hz.
out_altvoltage1_frequency:
This channel duplicates the channel 0 frequency
out_altvoltage2_frequency:
A frequency doubler generates frequencies from
8000000000 Hz to 16000000000 Hz.
out_altvoltage3_frequency:
A frequency quadrupler generates frequencies from
16000000000 Hz to 32000000000 Hz.
Note: writes to one of the channels will affect the frequency of
all the other channels, since it involves changing the VCO
fundamental output frequency.
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_phase What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_phase
KernelVersion: 3.4.0 KernelVersion: 3.4.0
@ -663,6 +695,17 @@ Description:
specified and unique if the output corresponds to a single specified and unique if the output corresponds to a single
channel. channel.
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw
Date: May 2012
KernelVersion: 3.5
Contact: Johan Hovold <jhovold@gmail.com>
Description:
Set/get output current for channel Y. Units after application
of scale and offset are milliamps.
For some devices current channels are used to specify
current supplied to elements used in taking a measurement
of a different type. E.g. LED currents.
What: /sys/bus/iio/devices/iio:deviceX/events What: /sys/bus/iio/devices/iio:deviceX/events
KernelVersion: 2.6.35 KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org
@ -1195,16 +1238,12 @@ Description:
The name of the trigger source being used, as per string given The name of the trigger source being used, as per string given
in /sys/class/iio/triggerY/name. in /sys/class/iio/triggerY/name.
What: /sys/bus/iio/devices/iio:deviceX/buffer/length
KernelVersion: 2.6.35
What: /sys/bus/iio/devices/iio:deviceX/bufferY/length What: /sys/bus/iio/devices/iio:deviceX/bufferY/length
KernelVersion: 5.11 KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org
Description: Description:
Number of scans contained by the buffer. Number of scans contained by the buffer.
What: /sys/bus/iio/devices/iio:deviceX/buffer/enable
KernelVersion: 2.6.35
What: /sys/bus/iio/devices/iio:deviceX/bufferY/enable What: /sys/bus/iio/devices/iio:deviceX/bufferY/enable
KernelVersion: 5.11 KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org
@ -1212,8 +1251,6 @@ Description:
Actually start the buffer capture up. Will start trigger Actually start the buffer capture up. Will start trigger
if first device and appropriate. if first device and appropriate.
What: /sys/bus/iio/devices/iio:deviceX/scan_elements
KernelVersion: 2.6.37
What: /sys/bus/iio/devices/iio:deviceX/bufferY What: /sys/bus/iio/devices/iio:deviceX/bufferY
KernelVersion: 5.11 KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org
@ -1224,34 +1261,6 @@ Description:
Since kernel 5.11 the scan_elements attributes are merged into Since kernel 5.11 the scan_elements attributes are merged into
the bufferY directory, to be configurable per buffer. the bufferY directory, to be configurable per buffer.
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_en
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_en
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_en
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_x_en
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_y_en
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_z_en
What: /sys/.../iio:deviceX/scan_elements/in_magn_x_en
What: /sys/.../iio:deviceX/scan_elements/in_magn_y_en
What: /sys/.../iio:deviceX/scan_elements/in_magn_z_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_tilt_comp_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_tilt_comp_en
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
What: /sys/.../iio:deviceX/scan_elements/in_pressure_en
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_en
What: /sys/.../iio:deviceX/scan_elements/in_proximity_en
KernelVersion: 2.6.37
What: /sys/.../iio:deviceX/bufferY/in_accel_x_en What: /sys/.../iio:deviceX/bufferY/in_accel_x_en
What: /sys/.../iio:deviceX/bufferY/in_accel_y_en What: /sys/.../iio:deviceX/bufferY/in_accel_y_en
What: /sys/.../iio:deviceX/bufferY/in_accel_z_en What: /sys/.../iio:deviceX/bufferY/in_accel_z_en
@ -1284,23 +1293,6 @@ Contact: linux-iio@vger.kernel.org
Description: Description:
Scan element control for triggered data capture. Scan element control for triggered data capture.
What: /sys/.../iio:deviceX/scan_elements/in_accel_type
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_type
What: /sys/.../iio:deviceX/scan_elements/in_magn_type
What: /sys/.../iio:deviceX/scan_elements/in_incli_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_type
What: /sys/.../iio:deviceX/scan_elements/in_proximity_type
KernelVersion: 2.6.37
What: /sys/.../iio:deviceX/bufferY/in_accel_type What: /sys/.../iio:deviceX/bufferY/in_accel_type
What: /sys/.../iio:deviceX/bufferY/in_anglvel_type What: /sys/.../iio:deviceX/bufferY/in_anglvel_type
What: /sys/.../iio:deviceX/bufferY/in_magn_type What: /sys/.../iio:deviceX/bufferY/in_magn_type
@ -1347,33 +1339,6 @@ Description:
If the type parameter can take one of a small set of values, If the type parameter can take one of a small set of values,
this attribute lists them. this attribute lists them.
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_x_index
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_y_index
What: /sys/.../iio:deviceX/scan_elements/in_anglvel_z_index
What: /sys/.../iio:deviceX/scan_elements/in_magn_x_index
What: /sys/.../iio:deviceX/scan_elements/in_magn_y_index
What: /sys/.../iio:deviceX/scan_elements/in_magn_z_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_magnetic_tilt_comp_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_from_north_true_tilt_comp_index
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_index
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_index
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_index
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_index
What: /sys/.../iio:deviceX/scan_elements/in_pressure_index
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_index
What: /sys/.../iio:deviceX/scan_elements/in_proximity_index
KernelVersion: 2.6.37
What: /sys/.../iio:deviceX/bufferY/in_voltageY_index What: /sys/.../iio:deviceX/bufferY/in_voltageY_index
What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_index What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_index
What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_index What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_index
@ -1613,8 +1578,6 @@ Description:
Specifies number of seconds in which we compute the steps Specifies number of seconds in which we compute the steps
that occur in order to decide if the consumer is making steps. that occur in order to decide if the consumer is making steps.
What: /sys/bus/iio/devices/iio:deviceX/buffer/watermark
KernelVersion: 4.2
What: /sys/bus/iio/devices/iio:deviceX/bufferY/watermark What: /sys/bus/iio/devices/iio:deviceX/bufferY/watermark
KernelVersion: 5.11 KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org
@ -1633,8 +1596,6 @@ Description:
the available samples after the timeout expires and thus have a the available samples after the timeout expires and thus have a
maximum delay guarantee. maximum delay guarantee.
What: /sys/bus/iio/devices/iio:deviceX/buffer/data_available
KernelVersion: 4.16
What: /sys/bus/iio/devices/iio:deviceX/bufferY/data_available What: /sys/bus/iio/devices/iio:deviceX/bufferY/data_available
KernelVersion: 5.11 KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org

View File

@ -1,28 +1,3 @@
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Stores the PLL frequency in Hz for channel Y.
Reading returns the actual frequency in Hz.
The ADF4371 has an integrated VCO with fundamendal output
frequency ranging from 4000000000 Hz 8000000000 Hz.
out_altvoltage0_frequency:
A divide by 1, 2, 4, 8, 16, 32 or circuit generates
frequencies from 62500000 Hz to 8000000000 Hz.
out_altvoltage1_frequency:
This channel duplicates the channel 0 frequency
out_altvoltage2_frequency:
A frequency doubler generates frequencies from
8000000000 Hz to 16000000000 Hz.
out_altvoltage3_frequency:
A frequency quadrupler generates frequencies from
16000000000 Hz to 32000000000 Hz.
Note: writes to one of the channels will affect the frequency of
all the other channels, since it involves changing the VCO
fundamental output frequency.
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_name What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_name
KernelVersion: KernelVersion:
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org
@ -34,11 +9,3 @@ Description:
out_altvoltage2_name: RF16x out_altvoltage2_name: RF16x
out_altvoltage3_name: RF32x out_altvoltage3_name: RF32x
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
This attribute allows the user to power down the PLL and it's
RFOut buffers.
Writing 1 causes the specified channel to power down.
Clearing returns to normal operation.

View File

@ -18,6 +18,8 @@ Description:
respectively which simply helper channels containing the respectively which simply helper channels containing the
calculated difference in the value of stage 1 - 2 and 3 - 4. calculated difference in the value of stage 1 - 2 and 3 - 4.
The values are expressed in 24-bit twos complement. The values are expressed in 24-bit twos complement.
The LED current for the stage is controlled via
out_currentY_raw.
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_offset What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_offset
Date: May 2016 Date: May 2016
@ -35,11 +37,3 @@ Contact: Andrew F. Davis <afd@ti.com>
Description: Description:
Get and set the resistance and the capacitance settings for the Get and set the resistance and the capacitance settings for the
Transimpedance Amplifier during the associated stage. Transimpedance Amplifier during the associated stage.
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw
Date: May 2016
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Get and set the LED current for the specified LED active during
this stage. Y is the specific stage number.

View File

@ -1,20 +0,0 @@
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias
KernelVersion: 5.8
Contact: linux-iio@vger.kernel.org
Description:
Hardware applied calibration offset (assumed to fix production
inaccuracies). Values represent a real physical offset expressed
in SI units (m/s^2 for accelerometer and rad/s for gyroscope).
What: /sys/bus/iio/devices/iio:deviceX/in_accel_calibbias_available
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_calibbias_available
KernelVersion: 5.8
Contact: linux-iio@vger.kernel.org
Description:
Range of available values for hardware offset. Values in SI
units (m/s^2 for accelerometer and rad/s for gyroscope).

View File

@ -41,14 +41,6 @@ Description:
Get the current light zone (0..4) as defined by the Get the current light zone (0..4) as defined by the
in_illuminance0_threshY_{falling,rising} thresholds. in_illuminance0_threshY_{falling,rising} thresholds.
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw
Date: May 2012
KernelVersion: 3.5
Contact: Johan Hovold <jhovold@gmail.com>
Description:
Get output current for channel Y (0..255), that is,
out_currentY_currentZ_raw, where Z is the current zone.
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_currentZ_raw What: /sys/bus/iio/devices/iio:deviceX/out_currentY_currentZ_raw
Date: May 2012 Date: May 2012
KernelVersion: 3.5 KernelVersion: 3.5
@ -59,3 +51,6 @@ Description:
These values correspond to the ALS-mapper target registers for These values correspond to the ALS-mapper target registers for
ALS-mapper Y + 1. ALS-mapper Y + 1.
Note that out_currentY_raw provides the current for the
current zone.

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/accel/adi,adis16201.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ADIS16201 Dual Axis Inclinometer and similar
maintainers:
- Jonathan Cameron <Jonathan.Cameron@huawei.com>
description: |
Two similar parts from external interface point of view.
SPI interface.
https://www.analog.com/en/products/adis16201.html
https://www.analog.com/en/products/adis16209.html
properties:
compatible:
enum:
- adi,adis16201
- adi,adis16209
reg:
maxItems: 1
interrupts:
maxItems: 1
spi-max-frequency: true
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi0 {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@0 {
compatible = "adi,adis16201";
reg = <0>;
spi-max-frequency = <2500000>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
};
};
...

View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/accel/bosch,bma220.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Bosch BMA220 Trixial Acceleration Sensor
maintainers:
- Jonathan Cameron <Jonathan.Cameron@huawei.com>
properties:
compatible:
enum:
- bosch,bma220
reg:
maxItems: 1
interrupts:
maxItems: 1
spi-max-frequency: true
vdda-supply: true
vddd-supply: true
vddio-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi0 {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@0 {
compatible = "bosch,bma220";
reg = <0>;
spi-max-frequency = <2500000>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
};
};
...

View File

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/accel/fsl,mma7455.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale MMA7455 and MMA7456 three axis accelerometers
maintainers:
- Joachim Eastwood <manabian@gmail.com>
- Jonathan Cameron <jic23@kernel.org>
description:
Devices support both SPI and I2C interfaces.
properties:
compatible:
enum:
- fsl,mma7455
- fsl,mma7456
reg:
maxItems: 1
avdd-supply: true
vddio-supply: true
interrupts:
minItems: 1
maxItems: 2
interrupt-names:
description:
Data ready is only available on INT1, but events can use either or
both pins. If not specified, first element assumed to correspond
to INT1 and second (where present) to INT2.
minItems: 1
maxItems: 2
items:
enum:
- "INT1"
- "INT2"
spi-max-frequency: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
# include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@18 {
compatible = "fsl,mma7455";
reg = <0x18>;
vddio-supply = <&iovdd>;
avdd-supply = <&avdd>;
interrupts = <57 IRQ_TYPE_EDGE_FALLING>, <58 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "INT2", "INT1";
};
};
- |
# include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@0 {
compatible = "fsl,mma7456";
reg = <0>;
spi-max-frequency = <10000000>;
vddio-supply = <&iovdd>;
avdd-supply = <&avdd>;
interrupts = <57 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "INT1";
};
};
...

View File

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

View File

@ -0,0 +1,44 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/accel/murata,sca3300.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Murata SCA3300 Accelerometer
description: |
3-axis industrial accelerometer with digital SPI interface
https://www.murata.com/en-global/products/sensor/accel/sca3300
maintainers:
- Tomas Melin <tomas.melin@vaisala.com>
properties:
compatible:
enum:
- murata,sca3300
reg:
maxItems: 1
spi-max-frequency:
maximum: 8000000
required:
- compatible
- reg
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
accelerometer@0 {
compatible = "murata,sca3300";
reg = <0x0>;
spi-max-frequency = <4000000>;
};
};
...

View File

@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/accel/nxp,fxls8962af.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP FXLS8962AF/FXLS8964AF Accelerometer driver
maintainers:
- Sean Nyekjaer <sean@geanix.com>
description: |
NXP FXLS8962AF/FXLS8964AF Accelerometer driver that supports
SPI and I2C interface.
https://www.nxp.com/docs/en/data-sheet/FXLS8962AF.pdf
https://www.nxp.com/docs/en/data-sheet/FXLS8964AF.pdf
properties:
compatible:
enum:
- nxp,fxls8962af
- nxp,fxls8964af
reg:
maxItems: 1
vdd-supply:
description: phandle to the regulator that provides power to the accelerometer
spi-max-frequency: true
interrupts:
maxItems: 1
interrupt-names:
enum:
- INT1
- INT2
drive-open-drain:
type: boolean
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
/* Example for a I2C device node */
accelerometer@62 {
compatible = "nxp,fxls8962af";
reg = <0x62>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT1";
};
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi0 {
#address-cells = <1>;
#size-cells = <0>;
/* Example for a SPI device node */
accelerometer@0 {
compatible = "nxp,fxls8962af";
reg = <0>;
spi-max-frequency = <4000000>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT1";
};
};

View File

@ -39,4 +39,16 @@ properties:
The first value specifies the positive input pin, the second The first value specifies the positive input pin, the second
specifies the negative input pin. specifies the negative input pin.
settling-time-us:
description:
Time between enabling the channel and first stable readings.
oversampling-ratio:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Oversampling is used as replacement of or addition to the low-pass filter.
In some cases, the desired filtering characteristics are a function the
device design and can interact with other characteristics such as
settling time.
additionalProperties: true additionalProperties: true

View File

@ -0,0 +1,48 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2019 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad7298.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD7298 ADC
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
description: |
Bindings for the Analog Devices AD7298 ADC device. Datasheet can be
found here:
https://www.analog.com/en/products/ad7298.html
properties:
compatible:
const: adi,ad7298
reg:
maxItems: 1
vref-supply: true
vdd-supply: true
spi-max-frequency: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad7298";
reg = <0>;
spi-max-frequency = <5000000>;
vref-supply = <&adc_vref>;
};
};
...

View File

@ -0,0 +1,174 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2019 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad7476.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AD7476 and similar simple SPI ADCs from multiple manufacturers.
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
description: |
A lot of simple SPI ADCs have very straight forward interfaces.
They typically don't provide a MOSI pin, simply reading out data
on MISO when the clock toggles.
properties:
compatible:
enum:
- adi,ad7091
- adi,ad7091r
- adi,ad7273
- adi,ad7274
- adi,ad7276
- adi,ad7277
- adi,ad7278
- adi,ad7466
- adi,ad7467
- adi,ad7468
- adi,ad7475
- adi,ad7476
- adi,ad7476a
- adi,ad7477
- adi,ad7477a
- adi,ad7478
- adi,ad7478a
- adi,ad7495
- adi,ad7910
- adi,ad7920
- adi,ad7940
- ti,adc081s
- ti,adc101s
- ti,adc121s
- ti,ads7866
- ti,ads7867
- ti,ads7868
- lltc,ltc2314-14
reg:
maxItems: 1
vcc-supply:
description:
Main powersupply voltage for the chips, sometimes referred to as VDD on
datasheets. If there is no separate vref-supply, then this is needed
to establish channel scaling.
vdrive-supply:
description:
Some devices have separate supply for their digital control side.
vref-supply:
description:
Some devices have a specific reference voltage supplied on a different pin
to the other supplies. Needed to be able to establish channel scaling
unless there is also an internal reference available (e.g. ad7091r)
spi-max-frequency: true
adi,conversion-start-gpios:
description: A GPIO used to trigger the start of a conversion
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
allOf:
# Devices where reference is vcc
- if:
properties:
compatible:
contains:
enum:
- adi,ad7091
- adi,ad7276
- adi,ad7277
- adi,ad7278
- adi,ad7466
- adi,ad7467
- adi,ad7468
- adi,ad7940
- ti,adc081s
- ti,adc101s
- ti,adc121s
- ti,ads7866
- ti,ads7868
required:
- vcc-supply
# Devices with a vref
- if:
properties:
compatible:
contains:
enum:
- adi,ad7091r
- adi,ad7273
- adi,ad7274
- adi,ad7475
- lltc,ltc2314-14
then:
properties:
vref-supply: true
else:
properties:
vref-supply: false
# Devices with a vref where it is not optional
- if:
properties:
compatible:
contains:
enum:
- adi,ad7273
- adi,ad7274
- adi,ad7475
- lltc,ltc2314-14
then:
required:
- vref-supply
- if:
properties:
compatible:
contains:
enum:
- adi,ad7475
- adi,ad7495
then:
properties:
vdrive-supply: true
else:
properties:
vdrive-supply: false
- if:
properties:
compatible:
contains:
enum:
- adi,ad7091
- adi,ad7091r
then:
properties:
adi,conversion-start-gpios: true
else:
properties:
adi,conversion-start-gpios: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad7091r";
reg = <0>;
spi-max-frequency = <5000000>;
vcc-supply = <&adc_vcc>;
vref-supply = <&adc_vref>;
};
};
...

View File

@ -0,0 +1,115 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,tsc2046.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TSC2046 touch screen controller.
maintainers:
- Oleksij Rempel <o.rempel@pengutronix.de>
description: |
TSC2046 is a touch screen controller with 8 channels ADC.
properties:
compatible:
enum:
- ti,tsc2046e-adc
reg:
maxItems: 1
interrupts:
maxItems: 1
spi-max-frequency: true
"#io-channel-cells":
const: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
required:
- compatible
- reg
patternProperties:
"^channel@[0-7]$":
$ref: "adc.yaml"
type: object
properties:
reg:
description: |
The channel number. It can have up to 8 channels
items:
minimum: 0
maximum: 7
settling-time-us: true
oversampling-ratio: true
required:
- reg
additionalProperties: false
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "ti,tsc2046e-adc";
reg = <0>;
spi-max-frequency = <1000000>;
interrupts-extended = <&gpio3 20 IRQ_TYPE_LEVEL_LOW>;
#io-channel-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
};
channel@1 {
reg = <1>;
settling-time-us = <700>;
oversampling-ratio = <5>;
};
channel@2 {
reg = <2>;
};
channel@3 {
reg = <3>;
settling-time-us = <700>;
oversampling-ratio = <5>;
};
channel@4 {
reg = <4>;
settling-time-us = <700>;
oversampling-ratio = <5>;
};
channel@5 {
reg = <5>;
settling-time-us = <700>;
oversampling-ratio = <5>;
};
channel@6 {
reg = <6>;
};
channel@7 {
reg = <7>;
};
};
};
...

View File

@ -24,6 +24,9 @@ properties:
description: | description: |
Channel node of a voltage io-channel. Channel node of a voltage io-channel.
"#io-channel-cells":
const: 0
shunt-resistor-micro-ohms: shunt-resistor-micro-ohms:
description: The shunt resistance. description: The shunt resistance.
@ -57,6 +60,7 @@ examples:
sysi { sysi {
compatible = "current-sense-shunt"; compatible = "current-sense-shunt";
io-channels = <&tiadc 0>; io-channels = <&tiadc 0>;
#io-channel-cells = <0>;
/* Divide the voltage by 3300000/1000000 (or 3.3) for the current. */ /* Divide the voltage by 3300000/1000000 (or 3.3) for the current. */
shunt-resistor-micro-ohms = <3300000>; shunt-resistor-micro-ohms = <3300000>;

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/cdc/adi,ad7746.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AD7746 24-Bit Capacitance-to-Digital Converter with Temperature Sensor
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
description: |
AD7746 24-Bit Capacitance-to-Digital Converter with Temperature Sensor
Specifications about the part can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7291.pdf
properties:
compatible:
enum:
- adi,ad7745
- adi,ad7746
- adi,ad7747
reg:
maxItems: 1
adi,excitation-vdd-permille:
description: |
Set VDD per mille to be used as the excitation voltage.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [125, 250, 375, 500]
adi,exca-output-en:
description: Enables the EXCA pin as the excitation output.
type: boolean
adi,exca-output-invert:
description: |
Inverts the excitation output in the EXCA pin.
Normally only one of the EXCX pins would be inverted, check the following
application notes for more details
https://www.analog.com/media/en/technical-documentation/application-notes/AN-1585.pdf
type: boolean
adi,excb-output-en:
description: Enables the EXCB pin as the excitation output.
type: boolean
adi,excb-output-invert:
description: Inverts the excitation output in the EXCB pin.
type: boolean
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
ad7746: cdc@48 {
compatible = "adi,ad7746";
reg = <0x48>;
adi,excitation-vdd-permille = <125>;
adi,exca-output-en;
adi,exca-output-invert;
adi,excb-output-en;
adi,excb-output-invert;
};
};
...

View File

@ -22,7 +22,6 @@ properties:
required: required:
- compatible - compatible
- reg
additionalProperties: false additionalProperties: false
@ -37,5 +36,11 @@ examples:
reg = <0x69>; reg = <0x69>;
}; };
}; };
- |
serial {
air-pollution-sensor {
compatible = "sensirion,sps30";
};
};
... ...

View File

@ -1,124 +0,0 @@
* Analog Devices AD5755 IIO Multi-Channel DAC Linux Driver
Required properties:
- compatible: Has to contain one of the following:
adi,ad5755
adi,ad5755-1
adi,ad5757
adi,ad5735
adi,ad5737
- reg: spi chip select number for the device
- spi-cpha or spi-cpol: is the only modes that is supported
Recommended properties:
- spi-max-frequency: Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
See include/dt-bindings/iio/ad5755.h
- adi,ext-dc-dc-compenstation-resistor: boolean set if the hardware have an
external resistor and thereby bypasses
the internal compensation resistor.
- adi,dc-dc-phase:
Valid values for DC DC Phase control is:
0: All dc-to-dc converters clock on the same edge.
1: Channel A and Channel B clock on the same edge,
Channel C and Channel D clock on opposite edges.
2: Channel A and Channel C clock on the same edge,
Channel B and Channel D clock on opposite edges.
3: Channel A, Channel B, Channel C, and Channel D
clock 90 degrees out of phase from each other.
- adi,dc-dc-freq-hz:
Valid values for DC DC frequency is [Hz]:
250000
410000
650000
- adi,dc-dc-max-microvolt:
Valid values for the maximum allowed Vboost voltage supplied by
the dc-to-dc converter is:
23000000
24500000
27000000
29500000
Optional for every channel:
- adi,mode:
Valid values for DAC modes is:
0: 0 V to 5 V voltage range.
1: 0 V to 10 V voltage range.
2: Plus minus 5 V voltage range.
3: Plus minus 10 V voltage range.
4: 4 mA to 20 mA current range.
5: 0 mA to 20 mA current range.
6: 0 mA to 24 mA current range.
- adi,ext-current-sense-resistor: boolean set if the hardware a external
current sense resistor.
- adi,enable-voltage-overrange: boolean enable voltage overrange
- adi,slew: Array of slewrate settings should contain 3 fields:
1: Should be either 0 or 1 in order to enable or disable slewrate.
2: Slew rate settings:
Valid values for the slew rate update frequency:
64000
32000
16000
8000
4000
2000
1000
500
250
125
64
32
16
8
4
0
3: Slew step size:
Valid values for the step size LSBs:
1
2
4
16
32
64
128
256
Example:
dac@0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "adi,ad5755";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpha;
adi,dc-dc-phase = <0>;
adi,dc-dc-freq-hz = <410000>;
adi,dc-dc-max-microvolt = <23000000>;
channel@0 {
reg = <0>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
channel@1 {
reg = <1>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
channel@2 {
reg = <2>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
channel@3 {
reg = <3>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
};

View File

@ -0,0 +1,169 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/adi,ad5755.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD5755 Multi-Channel DAC
maintainers:
- Sean Nyekjaer <sean.nyekjaer@prevas.dk>
properties:
compatible:
enum:
- adi,ad5755
- adi,ad5755-1
- adi,ad5757
- adi,ad5735
- adi,ad5737
reg:
maxItems: 1
spi-cpha:
description: Either this or spi-cpol but not both.
spi-cpol: true
spi-max-frequency: true
adi,ext-dc-dc-compenstation-resistor:
$ref: /schemas/types.yaml#/definitions/flag
description:
Set if the hardware have an external resistor and thereby bypasses
the internal compensation resistor.
adi,dc-dc-phase:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
description: |
Valid values for DC DC Phase control is:
0: All dc-to-dc converters clock on the same edge.
1: Channel A and Channel B clock on the same edge,
Channel C and Channel D clock on opposite edges.
2: Channel A and Channel C clock on the same edge,
Channel B and Channel D clock on opposite edges.
3: Channel A, Channel B, Channel C, and Channel D
clock 90 degrees out of phase from each other.
adi,dc-dc-freq-hz:
enum: [250000, 410000, 650000]
adi,dc-dc-max-microvolt:
description:
Maximum allowed Vboost voltage supplied by the dc-to-dc converter.
enum: [23000000, 24500000, 27000000, 29500000]
"#address-cells":
const: 1
"#size-cells":
const: 0
"#io-channel-cells":
const: 1
required:
- compatible
- reg
additionalProperties: false
patternProperties:
"^channel@[0-7]$":
type: object
description: Child node to describe a channel
properties:
reg:
maxItems: 1
adi,mode:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 6
description: |
Valid values for DAC modes is:
0: 0 V to 5 V voltage range.
1: 0 V to 10 V voltage range.
2: Plus minus 5 V voltage range.
3: Plus minus 10 V voltage range.
4: 4 mA to 20 mA current range.
5: 0 mA to 20 mA current range.
6: 0 mA to 24 mA current range.
adi,ext-current-sense-resistor:
$ref: /schemas/types.yaml#/definitions/flag
description:
Set if the hardware has an external current sense resistor
adi,enable-voltage-overrange:
$ref: /schemas/types.yaml#/definitions/flag
description: Enable voltage overrange
adi,slew:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: |
Array of slewrate settings should contain 3 fields:
1: Should be either 0 or 1 in order to enable or disable slewrate.
2: Slew rate update frequency
3: Slew step size
items:
- enum: [0, 1]
- enum: [64000, 32000, 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 64, 32, 16, 8, 4, 0]
- enum: [1, 2, 4, 16, 32, 64, 128, 256]
required:
- reg
additionalProperties: false
oneOf:
- required:
- spi-cpha
- required:
- spi-cpol
examples:
- |
#include <dt-bindings/iio/adi,ad5592r.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "adi,ad5755";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpha;
adi,dc-dc-phase = <0>;
adi,dc-dc-freq-hz = <410000>;
adi,dc-dc-max-microvolt = <23000000>;
channel@0 {
reg = <0>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
channel@1 {
reg = <1>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
channel@2 {
reg = <2>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
channel@3 {
reg = <3>;
adi,mode = <4>;
adi,ext-current-sense-resistor;
adi,slew = <0 64000 1>;
};
};
};
...

View File

@ -0,0 +1,72 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/ti,dac082s085.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments DAC082s085 and similar DACs
description:
A family of Texas Instruments 8/10/12-bit 2/4-channel DACs
maintainers:
- Lukas Wunner <lukas@wunner.de>
properties:
compatible:
enum:
- ti,dac082s085
- ti,dac102s085
- ti,dac122s085
- ti,dac084s085
- ti,dac104s085
- ti,dac124s085
reg:
maxItems: 1
spi-cpha: true
spi-cpol:
description:
Must be either spi-cpha, or spi-cpol but not both.
vref-supply:
description: Needed to provide output scaling.
spi-max-frequency: true
required:
- compatible
- reg
- vref-supply
additionalProperties: false
oneOf:
- required:
- spi-cpha
- required:
- spi-cpol
examples:
- |
vref_2v5_reg: regulator-vref {
compatible = "regulator-fixed";
regulator-name = "2v5";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
regulator-always-on;
};
spi {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
compatible = "ti,dac082s085";
reg = <0>;
spi-max-frequency = <40000000>;
spi-cpol;
vref-supply = <&vref_2v5_reg>;
};
};
...

View File

@ -1,34 +0,0 @@
Texas Instruments 8/10/12-bit 2/4-channel DAC driver
Required properties:
- compatible: Must be one of:
"ti,dac082s085"
"ti,dac102s085"
"ti,dac122s085"
"ti,dac084s085"
"ti,dac104s085"
"ti,dac124s085"
- reg: Chip select number.
- spi-cpha, spi-cpol: SPI mode (0,1) or (1,0) must be used, so specify
either spi-cpha or spi-cpol (but not both).
- vref-supply: Phandle to the external reference voltage supply.
For other required and optional properties of SPI slave nodes please refer to
../../spi/spi-bus.txt.
Example:
vref_2v5_reg: regulator-vref {
compatible = "regulator-fixed";
regulator-name = "2v5";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
regulator-always-on;
};
dac@0 {
compatible = "ti,dac082s085";
reg = <0>;
spi-max-frequency = <40000000>;
spi-cpol;
vref-supply = <&vref_2v5_reg>;
};

View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/amstaos,tsl2591.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AMS/TAOS TSL2591 Ambient Light Sensor (ALS)
maintainers:
- Joe Sandom <joe.g.sandom@gmail.com>
description: |
AMS/TAOS TSL2591 is a very-high sensitivity
light-to-digital converter that transforms light intensity into a digital
signal.
properties:
compatible:
const: amstaos,tsl2591
reg:
maxItems: 1
interrupts:
maxItems: 1
description:
Interrupt (INT:Pin 2) Active low. Should be set to IRQ_TYPE_EDGE_FALLING.
interrupt is used to detect if the light intensity has fallen below
or reached above the configured threshold values.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
tsl2591@29 {
compatible = "amstaos,tsl2591";
reg = <0x29>;
interrupts = <20 IRQ_TYPE_EDGE_FALLING>;
};
};
...

View File

@ -6,7 +6,9 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics MEMS sensors title: STMicroelectronics MEMS sensors
description: | description: The STMicroelectronics sensor devices are pretty straight-forward
I2C or SPI devices, all sharing the same device tree descriptions no matter
what type of sensor it is.
Note that whilst this covers many STMicro MEMs sensors, some more complex Note that whilst this covers many STMicro MEMs sensors, some more complex
IMUs need their own bindings. IMUs need their own bindings.
The STMicroelectronics sensor devices are pretty straight-forward I2C or The STMicroelectronics sensor devices are pretty straight-forward I2C or
@ -15,90 +17,181 @@ description: |
maintainers: maintainers:
- Denis Ciocca <denis.ciocca@st.com> - Denis Ciocca <denis.ciocca@st.com>
- Linus Walleij <linus.walleij@linaro.org>
properties: properties:
compatible: compatible:
description: | oneOf:
Some values are deprecated. - description: STMicroelectronics Accelerometers
st,lis3lv02d (deprecated, use st,lis3lv02dl-accel) enum:
st,lis302dl-spi (deprecated, use st,lis3lv02dl-accel) - st,h3lis331dl-accel
enum: - st,lis2de12
# Accelerometers - st,lis2dw12
- st,lis3lv02d - st,lis2hh12
- st,lis302dl-spi - st,lis2dh12-accel
- st,lis3lv02dl-accel - st,lis331dl-accel
- st,lsm303dlh-accel - st,lis331dlh-accel
- st,lsm303dlhc-accel - st,lis3de
- st,lis3dh-accel - st,lis3dh-accel
- st,lsm330d-accel - st,lis3dhh
- st,lsm330dl-accel - st,lis3l02dq
- st,lsm330dlc-accel - st,lis3lv02dl-accel
- st,lis331dl-accel - st,lng2dm-accel
- st,lis331dlh-accel - st,lsm303agr-accel
- st,lsm303dl-accel - st,lsm303dl-accel
- st,lsm303dlm-accel - st,lsm303dlh-accel
- st,lsm330-accel - st,lsm303dlhc-accel
- st,lsm303agr-accel - st,lsm303dlm-accel
- st,lis2dh12-accel - st,lsm330-accel
- st,h3lis331dl-accel - st,lsm330d-accel
- st,lng2dm-accel - st,lsm330dl-accel
- st,lis3l02dq - st,lsm330dlc-accel
- st,lis2dw12 - description: STMicroelectronics Gyroscopes
- st,lis3dhh enum:
- st,lis3de - st,l3g4200d-gyro
- st,lis2de12 - st,l3g4is-gyro
- st,lis2hh12 - st,l3gd20-gyro
# Gyroscopes - st,l3gd20h-gyro
- st,l3g4200d-gyro - st,lsm330-gyro
- st,lsm330d-gyro - st,lsm330d-gyro
- st,lsm330dl-gyro - st,lsm330dl-gyro
- st,lsm330dlc-gyro - st,lsm330dlc-gyro
- st,l3gd20-gyro - st,lsm9ds0-gyro
- st,l3gd20h-gyro - description: STMicroelectronics Magnetometers
- st,l3g4is-gyro enum:
- st,lsm330-gyro - st,lis2mdl
- st,lsm9ds0-gyro - st,lis3mdl-magn
# Magnetometers - st,lsm303agr-magn
- st,lsm303agr-magn - st,lsm303dlh-magn
- st,lsm303dlh-magn - st,lsm303dlhc-magn
- st,lsm303dlhc-magn - st,lsm303dlm-magn
- st,lsm303dlm-magn - st,lsm9ds1-magn
- st,lis3mdl-magn - description: STMicroelectronics Pressure Sensors
- st,lis2mdl enum:
- st,lsm9ds1-magn - st,lps001wp-press
- st,iis2mdc - st,lps22hb-press
# Pressure sensors - st,lps22hh
- st,lps001wp-press - st,lps25h-press
- st,lps25h-press - st,lps331ap-press
- st,lps331ap-press - st,lps33hw
- st,lps22hb-press - st,lps35hw
- st,lps33hw - description: IMUs
- st,lps35hw enum:
- st,lps22hh - st,lsm9ds0-imu
- description: Deprecated bindings
enum:
- st,lis302dl-spi
- st,lis3lv02d
deprecated: true
reg: reg:
maxItems: 1 maxItems: 1
interrupts: interrupts:
description: interrupt line(s) connected to the DRDY line(s) and/or the
Intertial interrupt lines INT1 and INT2 if these exist. This means up to
three interrupts, and the DRDY must be the first one if it exists on
the package. The trigger edge of the interrupts is sometimes software
configurable in the hardware so the operating system should parse this
flag and set up the trigger edge as indicated in the device tree.
minItems: 1 minItems: 1
maxItems: 2
vdd-supply: true vdd-supply: true
vddio-supply: true vddio-supply: true
st,drdy-int-pin: st,drdy-int-pin:
description: the pin on the package that will be used to signal
"data ready" (valid values 1 or 2). This property is not configurable
on all sensors.
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
description: enum: [1, 2]
Some sensors have multiple possible pins via which they can provide
a data ready interrupt. This selects which one.
enum:
- 1
- 2
drive-open-drain: drive-open-drain:
$ref: /schemas/types.yaml#/definitions/flag $ref: /schemas/types.yaml#/definitions/flag
description: | description: the interrupt/data ready line will be configured
The interrupt/data ready line will be configured as open drain, which as open drain, which is useful if several sensors share the same
is useful if several sensors share the same interrupt line. interrupt line. (This binding is taken from pinctrl.)
mount-matrix:
description: an optional 3x3 mounting rotation matrix.
allOf:
- if:
properties:
compatible:
enum:
# These have no interrupts
- st,lps001wp
then:
properties:
interrupts: false
st,drdy-int-pin: false
drive-open-drain: false
- if:
properties:
compatible:
enum:
# These have only DRDY
- st,lis2mdl
- st,lis3l02dq
- st,lis3lv02dl-accel
- st,lps22hb-press
- st,lps22hh
- st,lps25h-press
- st,lps33hw
- st,lps35hw
- st,lsm303agr-magn
- st,lsm303dlh-magn
- st,lsm303dlhc-magn
- st,lsm303dlm-magn
then:
properties:
interrupts:
maxItems: 1
st,drdy-int-pin: false
- if:
properties:
compatible:
enum:
# Two intertial interrupts i.e. accelerometer/gyro interrupts
- st,h3lis331dl-accel
- st,l3g4200d-gyro
- st,l3g4is-gyro
- st,l3gd20-gyro
- st,l3gd20h-gyro
- st,lis2de12
- st,lis2dw12
- st,lis2hh12
- st,lis2dh12-accel
- st,lis331dl-accel
- st,lis331dlh-accel
- st,lis3de
- st,lis3dh-accel
- st,lis3dhh
- st,lis3mdl-magn
- st,lng2dm-accel
- st,lps331ap-press
- st,lsm303agr-accel
- st,lsm303dlh-accel
- st,lsm303dlhc-accel
- st,lsm303dlm-accel
- st,lsm330-accel
- st,lsm330-gyro
- st,lsm330d-accel
- st,lsm330d-gyro
- st,lsm330dl-accel
- st,lsm330dl-gyro
- st,lsm330dlc-accel
- st,lsm330dlc-gyro
- st,lsm9ds0-gyro
- st,lsm9ds1-magn
then:
properties:
interrupts:
maxItems: 2
required: required:
- compatible - compatible
@ -110,15 +203,30 @@ examples:
- | - |
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>
i2c { i2c {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
accelerometer@1d {
compatible = "st,lis3lv02dl-accel"; accelerometer@1c {
reg = <0x1d>; compatible = "st,lis331dl-accel";
interrupt-parent = <&gpio2>; reg = <0x1c>;
interrupts = <18 IRQ_TYPE_EDGE_RISING>; st,drdy-int-pin = <1>;
pinctrl-0 = <&lis3lv02dl_nhk_mode>; vdd-supply = <&ldo1>;
pinctrl-names = "default"; vddio-supply = <&ldo2>;
}; interrupt-parent = <&gpio>;
interrupts = <18 IRQ_TYPE_EDGE_RISING>, <19 IRQ_TYPE_EDGE_RISING>;
};
};
spi {
#address-cells = <1>;
#size-cells = <0>;
num-cs = <1>;
l3g4200d: gyroscope@0 {
compatible = "st,l3g4200d-gyro";
st,drdy-int-pin = <2>;
reg = <0>;
vdd-supply = <&vcc_io>;
vddio-supply = <&vcc_io>;
};
}; };
... ...

View File

@ -0,0 +1,41 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/iio/temperature/ti,tmp117.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: "TI TMP117 - Digital temperature sensor with integrated NV memory"
description: |
TI TMP117 - Digital temperature sensor with integrated NV memory that supports
I2C interface.
https://www.ti.com/lit/gpn/tmp1
maintainers:
- Puranjay Mohan <puranjay12@gmail.com>
properties:
compatible:
enum:
- ti,tmp117
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
tmp117@48 {
compatible = "ti,tmp117";
reg = <0x48>;
};
};

View File

@ -173,8 +173,14 @@ properties:
- meas,tsys01 - meas,tsys01
# MEMSIC magnetometer # MEMSIC magnetometer
- memsic,mmc35240 - memsic,mmc35240
# MEMSIC 3-axis accelerometer
- memsic,mx4005
# MEMSIC 2-axis 8-bit digital accelerometer # MEMSIC 2-axis 8-bit digital accelerometer
- memsic,mxc6225 - memsic,mxc6225
# MEMSIC 2-axis 8-bit digital accelerometer
- memsic,mxc6255
# MEMSIC 3-axis accelerometer
- memsic,mxc6655
# Microchip differential I2C ADC, 1 Channel, 18 bit # Microchip differential I2C ADC, 1 Channel, 18 bit
- microchip,mcp3421 - microchip,mcp3421
# Microchip differential I2C ADC, 2 Channel, 18 bit # Microchip differential I2C ADC, 2 Channel, 18 bit
@ -259,6 +265,10 @@ properties:
- sensirion,sgpc3 - sensirion,sgpc3
# Sensirion multi-pixel gas sensor with I2C interface # Sensirion multi-pixel gas sensor with I2C interface
- sensirion,sgp30 - sensirion,sgp30
# Sensortek 3 axis accelerometer
- sensortek,stk8312
# Sensortek 3 axis accelerometer
- sensortek,stk8ba50
# SGX Sensortech VZ89X Sensors # SGX Sensortech VZ89X Sensors
- sgx,vz89x - sgx,vz89x
# Relative Humidity and Temperature Sensors # Relative Humidity and Temperature Sensors

View File

@ -9371,6 +9371,11 @@ L: linux-pm@vger.kernel.org
S: Supported S: Supported
F: drivers/cpufreq/intel_pstate.c F: drivers/cpufreq/intel_pstate.c
INTEL QUADRATURE ENCODER PERIPHERAL DRIVER
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
L: linux-iio@vger.kernel.org
F: drivers/counter/intel-qep.c
INTEL RDMA RNIC DRIVER INTEL RDMA RNIC DRIVER
M: Faisal Latif <faisal.latif@intel.com> M: Faisal Latif <faisal.latif@intel.com>
M: Shiraz Saleem <shiraz.saleem@intel.com> M: Shiraz Saleem <shiraz.saleem@intel.com>
@ -16494,6 +16499,8 @@ M: Tomasz Duszynski <tduszyns@gmail.com>
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml F: Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml
F: drivers/iio/chemical/sps30.c F: drivers/iio/chemical/sps30.c
F: drivers/iio/chemical/sps30_i2c.c
F: drivers/iio/chemical/sps30_serial.c
SERIAL DEVICE BUS SERIAL DEVICE BUS
M: Rob Herring <robh@kernel.org> M: Rob Herring <robh@kernel.org>
@ -18109,6 +18116,13 @@ F: Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
F: Documentation/hwmon/tps23861.rst F: Documentation/hwmon/tps23861.rst
F: drivers/hwmon/tps23861.c F: drivers/hwmon/tps23861.c
TEXAS INSTRUMENTS' TMP117 TEMPERATURE SENSOR DRIVER
M: Puranjay Mohan <puranjay12@gmail.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml
F: drivers/iio/temperature/tmp117.c
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl> M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org L: linux-media@vger.kernel.org
@ -18338,6 +18352,14 @@ S: Supported
F: Documentation/devicetree/bindings/net/nfc/trf7970a.txt F: Documentation/devicetree/bindings/net/nfc/trf7970a.txt
F: drivers/nfc/trf7970a.c F: drivers/nfc/trf7970a.c
TI TSC2046 ADC DRIVER
M: Oleksij Rempel <o.rempel@pengutronix.de>
R: kernel@pengutronix.de
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml
F: drivers/iio/adc/ti-tsc2046.c
TI TWL4030 SERIES SOC CODEC DRIVER TI TWL4030 SERIES SOC CODEC DRIVER
M: Peter Ujfalusi <peter.ujfalusi@gmail.com> M: Peter Ujfalusi <peter.ujfalusi@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)

View File

@ -91,4 +91,14 @@ config MICROCHIP_TCB_CAPTURE
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called microchip-tcb-capture. module will be called microchip-tcb-capture.
config INTEL_QEP
tristate "Intel Quadrature Encoder Peripheral driver"
depends on PCI
help
Select this option to enable the Intel Quadrature Encoder Peripheral
driver.
To compile this driver as a module, choose M here: the module
will be called intel-qep.
endif # COUNTER endif # COUNTER

View File

@ -12,3 +12,4 @@ obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_TI_EQEP) += ti-eqep.o
obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o
obj-$(CONFIG_INTEL_QEP) += intel-qep.o

546
drivers/counter/intel-qep.c Normal file
View File

@ -0,0 +1,546 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Quadrature Encoder Peripheral driver
*
* Copyright (C) 2019-2021 Intel Corporation
*
* Author: Felipe Balbi (Intel)
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
* Author: Raymond Tan <raymond.tan@intel.com>
*/
#include <linux/bitops.h>
#include <linux/counter.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#define INTEL_QEPCON 0x00
#define INTEL_QEPFLT 0x04
#define INTEL_QEPCOUNT 0x08
#define INTEL_QEPMAX 0x0c
#define INTEL_QEPWDT 0x10
#define INTEL_QEPCAPDIV 0x14
#define INTEL_QEPCNTR 0x18
#define INTEL_QEPCAPBUF 0x1c
#define INTEL_QEPINT_STAT 0x20
#define INTEL_QEPINT_MASK 0x24
/* QEPCON */
#define INTEL_QEPCON_EN BIT(0)
#define INTEL_QEPCON_FLT_EN BIT(1)
#define INTEL_QEPCON_EDGE_A BIT(2)
#define INTEL_QEPCON_EDGE_B BIT(3)
#define INTEL_QEPCON_EDGE_INDX BIT(4)
#define INTEL_QEPCON_SWPAB BIT(5)
#define INTEL_QEPCON_OP_MODE BIT(6)
#define INTEL_QEPCON_PH_ERR BIT(7)
#define INTEL_QEPCON_COUNT_RST_MODE BIT(8)
#define INTEL_QEPCON_INDX_GATING_MASK GENMASK(10, 9)
#define INTEL_QEPCON_INDX_GATING(n) (((n) & 3) << 9)
#define INTEL_QEPCON_INDX_PAL_PBL INTEL_QEPCON_INDX_GATING(0)
#define INTEL_QEPCON_INDX_PAL_PBH INTEL_QEPCON_INDX_GATING(1)
#define INTEL_QEPCON_INDX_PAH_PBL INTEL_QEPCON_INDX_GATING(2)
#define INTEL_QEPCON_INDX_PAH_PBH INTEL_QEPCON_INDX_GATING(3)
#define INTEL_QEPCON_CAP_MODE BIT(11)
#define INTEL_QEPCON_FIFO_THRE_MASK GENMASK(14, 12)
#define INTEL_QEPCON_FIFO_THRE(n) ((((n) - 1) & 7) << 12)
#define INTEL_QEPCON_FIFO_EMPTY BIT(15)
/* QEPFLT */
#define INTEL_QEPFLT_MAX_COUNT(n) ((n) & 0x1fffff)
/* QEPINT */
#define INTEL_QEPINT_FIFOCRIT BIT(5)
#define INTEL_QEPINT_FIFOENTRY BIT(4)
#define INTEL_QEPINT_QEPDIR BIT(3)
#define INTEL_QEPINT_QEPRST_UP BIT(2)
#define INTEL_QEPINT_QEPRST_DOWN BIT(1)
#define INTEL_QEPINT_WDT BIT(0)
#define INTEL_QEPINT_MASK_ALL GENMASK(5, 0)
#define INTEL_QEP_CLK_PERIOD_NS 10
#define INTEL_QEP_COUNTER_EXT_RW(_name) \
{ \
.name = #_name, \
.read = _name##_read, \
.write = _name##_write, \
}
struct intel_qep {
struct counter_device counter;
struct mutex lock;
struct device *dev;
void __iomem *regs;
bool enabled;
/* Context save registers */
u32 qepcon;
u32 qepflt;
u32 qepmax;
};
static inline u32 intel_qep_readl(struct intel_qep *qep, u32 offset)
{
return readl(qep->regs + offset);
}
static inline void intel_qep_writel(struct intel_qep *qep,
u32 offset, u32 value)
{
writel(value, qep->regs + offset);
}
static void intel_qep_init(struct intel_qep *qep)
{
u32 reg;
reg = intel_qep_readl(qep, INTEL_QEPCON);
reg &= ~INTEL_QEPCON_EN;
intel_qep_writel(qep, INTEL_QEPCON, reg);
qep->enabled = false;
/*
* Make sure peripheral is disabled by flushing the write with
* a dummy read
*/
reg = intel_qep_readl(qep, INTEL_QEPCON);
reg &= ~(INTEL_QEPCON_OP_MODE | INTEL_QEPCON_FLT_EN);
reg |= INTEL_QEPCON_EDGE_A | INTEL_QEPCON_EDGE_B |
INTEL_QEPCON_EDGE_INDX | INTEL_QEPCON_COUNT_RST_MODE;
intel_qep_writel(qep, INTEL_QEPCON, reg);
intel_qep_writel(qep, INTEL_QEPINT_MASK, INTEL_QEPINT_MASK_ALL);
}
static int intel_qep_count_read(struct counter_device *counter,
struct counter_count *count,
unsigned long *val)
{
struct intel_qep *const qep = counter->priv;
pm_runtime_get_sync(qep->dev);
*val = intel_qep_readl(qep, INTEL_QEPCOUNT);
pm_runtime_put(qep->dev);
return 0;
}
static const enum counter_count_function intel_qep_count_functions[] = {
COUNTER_COUNT_FUNCTION_QUADRATURE_X4,
};
static int intel_qep_function_get(struct counter_device *counter,
struct counter_count *count,
size_t *function)
{
*function = 0;
return 0;
}
static const enum counter_synapse_action intel_qep_synapse_actions[] = {
COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
};
static int intel_qep_action_get(struct counter_device *counter,
struct counter_count *count,
struct counter_synapse *synapse,
size_t *action)
{
*action = 0;
return 0;
}
static const struct counter_ops intel_qep_counter_ops = {
.count_read = intel_qep_count_read,
.function_get = intel_qep_function_get,
.action_get = intel_qep_action_get,
};
#define INTEL_QEP_SIGNAL(_id, _name) { \
.id = (_id), \
.name = (_name), \
}
static struct counter_signal intel_qep_signals[] = {
INTEL_QEP_SIGNAL(0, "Phase A"),
INTEL_QEP_SIGNAL(1, "Phase B"),
INTEL_QEP_SIGNAL(2, "Index"),
};
#define INTEL_QEP_SYNAPSE(_signal_id) { \
.actions_list = intel_qep_synapse_actions, \
.num_actions = ARRAY_SIZE(intel_qep_synapse_actions), \
.signal = &intel_qep_signals[(_signal_id)], \
}
static struct counter_synapse intel_qep_count_synapses[] = {
INTEL_QEP_SYNAPSE(0),
INTEL_QEP_SYNAPSE(1),
INTEL_QEP_SYNAPSE(2),
};
static ssize_t ceiling_read(struct counter_device *counter,
struct counter_count *count,
void *priv, char *buf)
{
struct intel_qep *qep = counter->priv;
u32 reg;
pm_runtime_get_sync(qep->dev);
reg = intel_qep_readl(qep, INTEL_QEPMAX);
pm_runtime_put(qep->dev);
return sysfs_emit(buf, "%u\n", reg);
}
static ssize_t ceiling_write(struct counter_device *counter,
struct counter_count *count,
void *priv, const char *buf, size_t len)
{
struct intel_qep *qep = counter->priv;
u32 max;
int ret;
ret = kstrtou32(buf, 0, &max);
if (ret < 0)
return ret;
mutex_lock(&qep->lock);
if (qep->enabled) {
ret = -EBUSY;
goto out;
}
pm_runtime_get_sync(qep->dev);
intel_qep_writel(qep, INTEL_QEPMAX, max);
pm_runtime_put(qep->dev);
ret = len;
out:
mutex_unlock(&qep->lock);
return ret;
}
static ssize_t enable_read(struct counter_device *counter,
struct counter_count *count,
void *priv, char *buf)
{
struct intel_qep *qep = counter->priv;
return sysfs_emit(buf, "%u\n", qep->enabled);
}
static ssize_t enable_write(struct counter_device *counter,
struct counter_count *count,
void *priv, const char *buf, size_t len)
{
struct intel_qep *qep = counter->priv;
u32 reg;
bool val, changed;
int ret;
ret = kstrtobool(buf, &val);
if (ret)
return ret;
mutex_lock(&qep->lock);
changed = val ^ qep->enabled;
if (!changed)
goto out;
pm_runtime_get_sync(qep->dev);
reg = intel_qep_readl(qep, INTEL_QEPCON);
if (val) {
/* Enable peripheral and keep runtime PM always on */
reg |= INTEL_QEPCON_EN;
pm_runtime_get_noresume(qep->dev);
} else {
/* Let runtime PM be idle and disable peripheral */
pm_runtime_put_noidle(qep->dev);
reg &= ~INTEL_QEPCON_EN;
}
intel_qep_writel(qep, INTEL_QEPCON, reg);
pm_runtime_put(qep->dev);
qep->enabled = val;
out:
mutex_unlock(&qep->lock);
return len;
}
static ssize_t spike_filter_ns_read(struct counter_device *counter,
struct counter_count *count,
void *priv, char *buf)
{
struct intel_qep *qep = counter->priv;
u32 reg;
pm_runtime_get_sync(qep->dev);
reg = intel_qep_readl(qep, INTEL_QEPCON);
if (!(reg & INTEL_QEPCON_FLT_EN)) {
pm_runtime_put(qep->dev);
return sysfs_emit(buf, "0\n");
}
reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT));
pm_runtime_put(qep->dev);
return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS);
}
static ssize_t spike_filter_ns_write(struct counter_device *counter,
struct counter_count *count,
void *priv, const char *buf, size_t len)
{
struct intel_qep *qep = counter->priv;
u32 reg, length;
bool enable;
int ret;
ret = kstrtou32(buf, 0, &length);
if (ret < 0)
return ret;
/*
* Spike filter length is (MAX_COUNT + 2) clock periods.
* Disable filter when userspace writes 0, enable for valid
* nanoseconds values and error out otherwise.
*/
length /= INTEL_QEP_CLK_PERIOD_NS;
if (length == 0) {
enable = false;
length = 0;
} else if (length >= 2) {
enable = true;
length -= 2;
} else {
return -EINVAL;
}
if (length > INTEL_QEPFLT_MAX_COUNT(length))
return -EINVAL;
mutex_lock(&qep->lock);
if (qep->enabled) {
ret = -EBUSY;
goto out;
}
pm_runtime_get_sync(qep->dev);
reg = intel_qep_readl(qep, INTEL_QEPCON);
if (enable)
reg |= INTEL_QEPCON_FLT_EN;
else
reg &= ~INTEL_QEPCON_FLT_EN;
intel_qep_writel(qep, INTEL_QEPFLT, length);
intel_qep_writel(qep, INTEL_QEPCON, reg);
pm_runtime_put(qep->dev);
ret = len;
out:
mutex_unlock(&qep->lock);
return ret;
}
static ssize_t preset_enable_read(struct counter_device *counter,
struct counter_count *count,
void *priv, char *buf)
{
struct intel_qep *qep = counter->priv;
u32 reg;
pm_runtime_get_sync(qep->dev);
reg = intel_qep_readl(qep, INTEL_QEPCON);
pm_runtime_put(qep->dev);
return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE));
}
static ssize_t preset_enable_write(struct counter_device *counter,
struct counter_count *count,
void *priv, const char *buf, size_t len)
{
struct intel_qep *qep = counter->priv;
u32 reg;
bool val;
int ret;
ret = kstrtobool(buf, &val);
if (ret)
return ret;
mutex_lock(&qep->lock);
if (qep->enabled) {
ret = -EBUSY;
goto out;
}
pm_runtime_get_sync(qep->dev);
reg = intel_qep_readl(qep, INTEL_QEPCON);
if (val)
reg &= ~INTEL_QEPCON_COUNT_RST_MODE;
else
reg |= INTEL_QEPCON_COUNT_RST_MODE;
intel_qep_writel(qep, INTEL_QEPCON, reg);
pm_runtime_put(qep->dev);
ret = len;
out:
mutex_unlock(&qep->lock);
return ret;
}
static const struct counter_count_ext intel_qep_count_ext[] = {
INTEL_QEP_COUNTER_EXT_RW(ceiling),
INTEL_QEP_COUNTER_EXT_RW(enable),
INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns),
INTEL_QEP_COUNTER_EXT_RW(preset_enable)
};
static struct counter_count intel_qep_counter_count[] = {
{
.id = 0,
.name = "Channel 1 Count",
.functions_list = intel_qep_count_functions,
.num_functions = ARRAY_SIZE(intel_qep_count_functions),
.synapses = intel_qep_count_synapses,
.num_synapses = ARRAY_SIZE(intel_qep_count_synapses),
.ext = intel_qep_count_ext,
.num_ext = ARRAY_SIZE(intel_qep_count_ext),
},
};
static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
struct intel_qep *qep;
struct device *dev = &pci->dev;
void __iomem *regs;
int ret;
qep = devm_kzalloc(dev, sizeof(*qep), GFP_KERNEL);
if (!qep)
return -ENOMEM;
ret = pcim_enable_device(pci);
if (ret)
return ret;
pci_set_master(pci);
ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci));
if (ret)
return ret;
regs = pcim_iomap_table(pci)[0];
if (!regs)
return -ENOMEM;
qep->dev = dev;
qep->regs = regs;
mutex_init(&qep->lock);
intel_qep_init(qep);
pci_set_drvdata(pci, qep);
qep->counter.name = pci_name(pci);
qep->counter.parent = dev;
qep->counter.ops = &intel_qep_counter_ops;
qep->counter.counts = intel_qep_counter_count;
qep->counter.num_counts = ARRAY_SIZE(intel_qep_counter_count);
qep->counter.signals = intel_qep_signals;
qep->counter.num_signals = ARRAY_SIZE(intel_qep_signals);
qep->counter.priv = qep;
qep->enabled = false;
pm_runtime_put(dev);
pm_runtime_allow(dev);
return devm_counter_register(&pci->dev, &qep->counter);
}
static void intel_qep_remove(struct pci_dev *pci)
{
struct intel_qep *qep = pci_get_drvdata(pci);
struct device *dev = &pci->dev;
pm_runtime_forbid(dev);
if (!qep->enabled)
pm_runtime_get(dev);
intel_qep_writel(qep, INTEL_QEPCON, 0);
}
#ifdef CONFIG_PM
static int intel_qep_suspend(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct intel_qep *qep = pci_get_drvdata(pdev);
qep->qepcon = intel_qep_readl(qep, INTEL_QEPCON);
qep->qepflt = intel_qep_readl(qep, INTEL_QEPFLT);
qep->qepmax = intel_qep_readl(qep, INTEL_QEPMAX);
return 0;
}
static int intel_qep_resume(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct intel_qep *qep = pci_get_drvdata(pdev);
/*
* Make sure peripheral is disabled when restoring registers and
* control register bits that are writable only when the peripheral
* is disabled
*/
intel_qep_writel(qep, INTEL_QEPCON, 0);
intel_qep_readl(qep, INTEL_QEPCON);
intel_qep_writel(qep, INTEL_QEPFLT, qep->qepflt);
intel_qep_writel(qep, INTEL_QEPMAX, qep->qepmax);
intel_qep_writel(qep, INTEL_QEPINT_MASK, INTEL_QEPINT_MASK_ALL);
/* Restore all other control register bits except enable status */
intel_qep_writel(qep, INTEL_QEPCON, qep->qepcon & ~INTEL_QEPCON_EN);
intel_qep_readl(qep, INTEL_QEPCON);
/* Restore enable status */
intel_qep_writel(qep, INTEL_QEPCON, qep->qepcon);
return 0;
}
#endif
static UNIVERSAL_DEV_PM_OPS(intel_qep_pm_ops,
intel_qep_suspend, intel_qep_resume, NULL);
static const struct pci_device_id intel_qep_id_table[] = {
/* EHL */
{ PCI_VDEVICE(INTEL, 0x4bc3), },
{ PCI_VDEVICE(INTEL, 0x4b81), },
{ PCI_VDEVICE(INTEL, 0x4b82), },
{ PCI_VDEVICE(INTEL, 0x4b83), },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, intel_qep_id_table);
static struct pci_driver intel_qep_driver = {
.name = "intel-qep",
.id_table = intel_qep_id_table,
.probe = intel_qep_probe,
.remove = intel_qep_remove,
.driver = {
.pm = &intel_qep_pm_ops,
}
};
module_pci_driver(intel_qep_driver);
MODULE_AUTHOR("Felipe Balbi (Intel)");
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel Quadrature Encoder Peripheral driver");

View File

@ -226,6 +226,33 @@ config DMARD10
Choosing M will build the driver as a module. If so, the module Choosing M will build the driver as a module. If so, the module
will be called dmard10. will be called dmard10.
config FXLS8962AF
tristate
config FXLS8962AF_I2C
tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver"
depends on I2C
select FXLS8962AF
select REGMAP_I2C
help
Say yes here to build support for the NXP 3-axis automotive
accelerometer FXLS8962AF/FXLS8964AF with I2C support.
To compile this driver as a module, choose M here: the module
will be called fxls8962af_i2c.
config FXLS8962AF_SPI
tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer SPI Driver"
depends on SPI
select FXLS8962AF
select REGMAP_SPI
help
Say yes here to build support for the NXP 3-axis automotive
accelerometer FXLS8962AF/FXLS8964AF with SPI support.
To compile this driver as a module, choose M here: the module
will be called fxls8962af_spi.
config HID_SENSOR_ACCEL_3D config HID_SENSOR_ACCEL_3D
depends on HID_SENSOR_HUB depends on HID_SENSOR_HUB
select IIO_BUFFER select IIO_BUFFER
@ -449,6 +476,19 @@ config SCA3000
To compile this driver as a module, say M here: the module will be To compile this driver as a module, say M here: the module will be
called sca3000. called sca3000.
config SCA3300
tristate "Murata SCA3300 3-Axis Accelerometer Driver"
depends on SPI
select CRC8
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Murata SCA3300 3-Axis
accelerometer.
To compile this driver as a module, choose M here: the module will be
called sca3300.
config STK8312 config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver" tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C depends on I2C

View File

@ -27,6 +27,9 @@ obj-$(CONFIG_DA311) += da311.o
obj-$(CONFIG_DMARD06) += dmard06.o obj-$(CONFIG_DMARD06) += dmard06.o
obj-$(CONFIG_DMARD09) += dmard09.o obj-$(CONFIG_DMARD09) += dmard09.o
obj-$(CONFIG_DMARD10) += dmard10.o obj-$(CONFIG_DMARD10) += dmard10.o
obj-$(CONFIG_FXLS8962AF) += fxls8962af-core.o
obj-$(CONFIG_FXLS8962AF_I2C) += fxls8962af-i2c.o
obj-$(CONFIG_FXLS8962AF_SPI) += fxls8962af-spi.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o obj-$(CONFIG_KXSD9) += kxsd9.o
@ -50,6 +53,7 @@ obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_MXC6255) += mxc6255.o obj-$(CONFIG_MXC6255) += mxc6255.o
obj-$(CONFIG_SCA3000) += sca3000.o obj-$(CONFIG_SCA3000) += sca3000.o
obj-$(CONFIG_SCA3300) += sca3300.o
obj-$(CONFIG_STK8312) += stk8312.o obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o obj-$(CONFIG_STK8BA50) += stk8ba50.o

View File

@ -8,10 +8,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/sysfs.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/imu/adis.h> #include <linux/iio/imu/adis.h>

View File

@ -7,11 +7,8 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/imu/adis.h> #include <linux/iio/imu/adis.h>

View File

@ -1223,14 +1223,14 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
st->dready_trig = devm_iio_trigger_alloc(dev, st->dready_trig = devm_iio_trigger_alloc(dev,
"%s-dev%d", "%s-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (st->dready_trig == NULL) if (st->dready_trig == NULL)
return -ENOMEM; return -ENOMEM;
st->peak_datardy_trig = devm_iio_trigger_alloc(dev, st->peak_datardy_trig = devm_iio_trigger_alloc(dev,
"%s-dev%d-peak", "%s-dev%d-peak",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!st->peak_datardy_trig) if (!st->peak_datardy_trig)
return -ENOMEM; return -ENOMEM;

View File

@ -162,7 +162,11 @@ struct bma180_data {
int scale; int scale;
int bw; int bw;
bool pmode; bool pmode;
u8 buff[16]; /* 3x 16-bit + 8-bit + padding + timestamp */ /* Ensure timestamp is naturally aligned */
struct {
s16 chan[4];
s64 timestamp __aligned(8);
} scan;
}; };
enum bma180_chan { enum bma180_chan {
@ -178,7 +182,7 @@ static int bma023_scale_table[] = { 2452, 4903, 9709, };
static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */ static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */
static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 }; static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 };
static int bma25x_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */ static int bma25x_bw_table[] = { 8, 16, 31, 63, 125, 250, 500, 1000 }; /* Hz */
static int bma25x_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, static int bma25x_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0,
0, 0, 306458 }; 0, 0, 306458 };
@ -938,12 +942,12 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
mutex_unlock(&data->mutex); mutex_unlock(&data->mutex);
goto err; goto err;
} }
((s16 *)data->buff)[i++] = ret; data->scan.chan[i++] = ret;
} }
mutex_unlock(&data->mutex); mutex_unlock(&data->mutex);
iio_push_to_buffers_with_timestamp(indio_dev, data->buff, time_ns); iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns);
err: err:
iio_trigger_notify_done(indio_dev->trig); iio_trigger_notify_done(indio_dev->trig);
@ -997,8 +1001,7 @@ static int bma180_probe(struct i2c_client *client,
chip = id->driver_data; chip = id->driver_data;
data->part_info = &bma180_part_info[chip]; data->part_info = &bma180_part_info[chip];
ret = iio_read_mount_matrix(dev, "mount-matrix", ret = iio_read_mount_matrix(dev, &data->orientation);
&data->orientation);
if (ret) if (ret)
return ret; return ret;
@ -1045,7 +1048,7 @@ static int bma180_probe(struct i2c_client *client,
if (client->irq > 0) { if (client->irq > 0) {
data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!data->trig) { if (!data->trig) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_chip_disable; goto err_chip_disable;

View File

@ -63,7 +63,11 @@ static const int bma220_scale_table[][2] = {
struct bma220_data { struct bma220_data {
struct spi_device *spi_device; struct spi_device *spi_device;
struct mutex lock; struct mutex lock;
s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 8x8 timestamp */ struct {
s8 chans[3];
/* Ensure timestamp is naturally aligned. */
s64 timestamp __aligned(8);
} scan;
u8 tx_buf[2] ____cacheline_aligned; u8 tx_buf[2] ____cacheline_aligned;
}; };
@ -94,12 +98,12 @@ static irqreturn_t bma220_trigger_handler(int irq, void *p)
mutex_lock(&data->lock); mutex_lock(&data->lock);
data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK; data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK;
ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer, ret = spi_write_then_read(spi, data->tx_buf, 1, &data->scan.chans,
ARRAY_SIZE(bma220_channels) - 1); ARRAY_SIZE(bma220_channels) - 1);
if (ret < 0) if (ret < 0)
goto err; goto err;
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
pf->timestamp); pf->timestamp);
err: err:
mutex_unlock(&data->lock); mutex_unlock(&data->lock);

View File

@ -811,7 +811,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
if (ret) if (ret)
return ret; return ret;
ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret) if (ret)
return ret; return ret;

View File

@ -389,7 +389,7 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
int ret; int ret;
if (on) { if (on) {
ret = pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
} else { } else {
pm_runtime_mark_last_busy(dev); pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev); ret = pm_runtime_put_autosuspend(dev);
@ -398,9 +398,6 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
if (ret < 0) { if (ret < 0) {
dev_err(dev, dev_err(dev,
"Failed: %s for %d\n", __func__, on); "Failed: %s for %d\n", __func__, on);
if (on)
pm_runtime_put_noidle(dev);
return ret; return ret;
} }
@ -1470,9 +1467,9 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
struct bmc150_accel_trigger *t = &data->triggers[i]; struct bmc150_accel_trigger *t = &data->triggers[i];
t->indio_trig = devm_iio_trigger_alloc(dev, t->indio_trig = devm_iio_trigger_alloc(dev,
bmc150_accel_triggers[i].name, bmc150_accel_triggers[i].name,
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!t->indio_trig) { if (!t->indio_trig) {
ret = -ENOMEM; ret = -ENOMEM;
break; break;
@ -1688,8 +1685,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
data->regmap = regmap; data->regmap = regmap;
if (!bmc150_apply_acpi_orientation(dev, &data->orientation)) { if (!bmc150_apply_acpi_orientation(dev, &data->orientation)) {
ret = iio_read_mount_matrix(dev, "mount-matrix", ret = iio_read_mount_matrix(dev, &data->orientation);
&data->orientation);
if (ret) if (ret)
return ret; return ret;
} }
@ -1836,7 +1832,6 @@ int bmc150_accel_core_remove(struct device *dev)
pm_runtime_disable(dev); pm_runtime_disable(dev);
pm_runtime_set_suspended(dev); pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);

View File

@ -285,11 +285,17 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
switch (chan->type) { switch (chan->type) {
case IIO_TEMP: case IIO_TEMP:
pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = bmi088_accel_get_temp(data, val); ret = bmi088_accel_get_temp(data, val);
goto out_read_raw_pm_put; goto out_read_raw_pm_put;
case IIO_ACCEL: case IIO_ACCEL:
pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = iio_device_claim_direct_mode(indio_dev); ret = iio_device_claim_direct_mode(indio_dev);
if (ret) if (ret)
goto out_read_raw_pm_put; goto out_read_raw_pm_put;
@ -319,7 +325,10 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
*val = BMI088_ACCEL_TEMP_UNIT; *val = BMI088_ACCEL_TEMP_UNIT;
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_ACCEL: case IIO_ACCEL:
pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = regmap_read(data->regmap, ret = regmap_read(data->regmap,
BMI088_ACCEL_REG_ACC_RANGE, val); BMI088_ACCEL_REG_ACC_RANGE, val);
if (ret) if (ret)
@ -334,7 +343,10 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
return -EINVAL; return -EINVAL;
} }
case IIO_CHAN_INFO_SAMP_FREQ: case IIO_CHAN_INFO_SAMP_FREQ:
pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = bmi088_accel_get_sample_freq(data, val, val2); ret = bmi088_accel_get_sample_freq(data, val, val2);
goto out_read_raw_pm_put; goto out_read_raw_pm_put;
default: default:
@ -376,7 +388,10 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: case IIO_CHAN_INFO_SAMP_FREQ:
pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = bmi088_accel_set_sample_freq(data, val); ret = bmi088_accel_set_sample_freq(data, val);
pm_runtime_mark_last_busy(dev); pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev); pm_runtime_put_autosuspend(dev);
@ -496,7 +511,6 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
if (ret) if (ret)
return ret; return ret;
indio_dev->dev.parent = dev;
indio_dev->channels = data->chip_info->channels; indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->num_channels; indio_dev->num_channels = data->chip_info->num_channels;
indio_dev->name = name ? name : data->chip_info->name; indio_dev->name = name ? name : data->chip_info->name;
@ -531,7 +545,6 @@ int bmi088_accel_core_remove(struct device *dev)
pm_runtime_disable(dev); pm_runtime_disable(dev);
pm_runtime_set_suspended(dev); pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
bmi088_accel_power_down(data); bmi088_accel_power_down(data);
return 0; return 0;

View File

@ -0,0 +1,968 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NXP FXLS8962AF/FXLS8964AF Accelerometer Core Driver
*
* Copyright 2021 Connected Cars A/S
*
* Datasheet:
* https://www.nxp.com/docs/en/data-sheet/FXLS8962AF.pdf
* https://www.nxp.com/docs/en/data-sheet/FXLS8964AF.pdf
*
* Errata:
* https://www.nxp.com/docs/en/errata/ES_FXLS8962AF.pdf
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include "fxls8962af.h"
#define FXLS8962AF_INT_STATUS 0x00
#define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0)
#define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5)
#define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7)
#define FXLS8962AF_TEMP_OUT 0x01
#define FXLS8962AF_VECM_LSB 0x02
#define FXLS8962AF_OUT_X_LSB 0x04
#define FXLS8962AF_OUT_Y_LSB 0x06
#define FXLS8962AF_OUT_Z_LSB 0x08
#define FXLS8962AF_BUF_STATUS 0x0b
#define FXLS8962AF_BUF_STATUS_BUF_CNT GENMASK(5, 0)
#define FXLS8962AF_BUF_STATUS_BUF_OVF BIT(6)
#define FXLS8962AF_BUF_STATUS_BUF_WMRK BIT(7)
#define FXLS8962AF_BUF_X_LSB 0x0c
#define FXLS8962AF_BUF_Y_LSB 0x0e
#define FXLS8962AF_BUF_Z_LSB 0x10
#define FXLS8962AF_PROD_REV 0x12
#define FXLS8962AF_WHO_AM_I 0x13
#define FXLS8962AF_SYS_MODE 0x14
#define FXLS8962AF_SENS_CONFIG1 0x15
#define FXLS8962AF_SENS_CONFIG1_ACTIVE BIT(0)
#define FXLS8962AF_SENS_CONFIG1_RST BIT(7)
#define FXLS8962AF_SC1_FSR_MASK GENMASK(2, 1)
#define FXLS8962AF_SC1_FSR_PREP(x) FIELD_PREP(FXLS8962AF_SC1_FSR_MASK, (x))
#define FXLS8962AF_SC1_FSR_GET(x) FIELD_GET(FXLS8962AF_SC1_FSR_MASK, (x))
#define FXLS8962AF_SENS_CONFIG2 0x16
#define FXLS8962AF_SENS_CONFIG3 0x17
#define FXLS8962AF_SC3_WAKE_ODR_MASK GENMASK(7, 4)
#define FXLS8962AF_SC3_WAKE_ODR_PREP(x) FIELD_PREP(FXLS8962AF_SC3_WAKE_ODR_MASK, (x))
#define FXLS8962AF_SC3_WAKE_ODR_GET(x) FIELD_GET(FXLS8962AF_SC3_WAKE_ODR_MASK, (x))
#define FXLS8962AF_SENS_CONFIG4 0x18
#define FXLS8962AF_SC4_INT_PP_OD_MASK BIT(1)
#define FXLS8962AF_SC4_INT_PP_OD_PREP(x) FIELD_PREP(FXLS8962AF_SC4_INT_PP_OD_MASK, (x))
#define FXLS8962AF_SC4_INT_POL_MASK BIT(0)
#define FXLS8962AF_SC4_INT_POL_PREP(x) FIELD_PREP(FXLS8962AF_SC4_INT_POL_MASK, (x))
#define FXLS8962AF_SENS_CONFIG5 0x19
#define FXLS8962AF_WAKE_IDLE_LSB 0x1b
#define FXLS8962AF_SLEEP_IDLE_LSB 0x1c
#define FXLS8962AF_ASLP_COUNT_LSB 0x1e
#define FXLS8962AF_INT_EN 0x20
#define FXLS8962AF_INT_EN_BUF_EN BIT(6)
#define FXLS8962AF_INT_PIN_SEL 0x21
#define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0)
#define FXLS8962AF_INT_PIN_SEL_INT1 0x00
#define FXLS8962AF_INT_PIN_SEL_INT2 GENMASK(7, 0)
#define FXLS8962AF_OFF_X 0x22
#define FXLS8962AF_OFF_Y 0x23
#define FXLS8962AF_OFF_Z 0x24
#define FXLS8962AF_BUF_CONFIG1 0x26
#define FXLS8962AF_BC1_BUF_MODE_MASK GENMASK(6, 5)
#define FXLS8962AF_BC1_BUF_MODE_PREP(x) FIELD_PREP(FXLS8962AF_BC1_BUF_MODE_MASK, (x))
#define FXLS8962AF_BUF_CONFIG2 0x27
#define FXLS8962AF_BUF_CONFIG2_BUF_WMRK GENMASK(5, 0)
#define FXLS8962AF_ORIENT_STATUS 0x28
#define FXLS8962AF_ORIENT_CONFIG 0x29
#define FXLS8962AF_ORIENT_DBCOUNT 0x2a
#define FXLS8962AF_ORIENT_BF_ZCOMP 0x2b
#define FXLS8962AF_ORIENT_THS_REG 0x2c
#define FXLS8962AF_SDCD_INT_SRC1 0x2d
#define FXLS8962AF_SDCD_INT_SRC2 0x2e
#define FXLS8962AF_SDCD_CONFIG1 0x2f
#define FXLS8962AF_SDCD_CONFIG2 0x30
#define FXLS8962AF_SDCD_OT_DBCNT 0x31
#define FXLS8962AF_SDCD_WT_DBCNT 0x32
#define FXLS8962AF_SDCD_LTHS_LSB 0x33
#define FXLS8962AF_SDCD_UTHS_LSB 0x35
#define FXLS8962AF_SELF_TEST_CONFIG1 0x37
#define FXLS8962AF_SELF_TEST_CONFIG2 0x38
#define FXLS8962AF_MAX_REG 0x38
#define FXLS8962AF_DEVICE_ID 0x62
#define FXLS8964AF_DEVICE_ID 0x84
/* Raw temp channel offset */
#define FXLS8962AF_TEMP_CENTER_VAL 25
#define FXLS8962AF_AUTO_SUSPEND_DELAY_MS 2000
#define FXLS8962AF_FIFO_LENGTH 32
#define FXLS8962AF_SCALE_TABLE_LEN 4
#define FXLS8962AF_SAMP_FREQ_TABLE_LEN 13
static const int fxls8962af_scale_table[FXLS8962AF_SCALE_TABLE_LEN][2] = {
{0, IIO_G_TO_M_S_2(980000)},
{0, IIO_G_TO_M_S_2(1950000)},
{0, IIO_G_TO_M_S_2(3910000)},
{0, IIO_G_TO_M_S_2(7810000)},
};
static const int fxls8962af_samp_freq_table[FXLS8962AF_SAMP_FREQ_TABLE_LEN][2] = {
{3200, 0}, {1600, 0}, {800, 0}, {400, 0}, {200, 0}, {100, 0},
{50, 0}, {25, 0}, {12, 500000}, {6, 250000}, {3, 125000},
{1, 563000}, {0, 781000},
};
struct fxls8962af_chip_info {
const char *name;
const struct iio_chan_spec *channels;
int num_channels;
u8 chip_id;
};
struct fxls8962af_data {
struct regmap *regmap;
const struct fxls8962af_chip_info *chip_info;
struct regulator *vdd_reg;
struct {
__le16 channels[3];
s64 ts __aligned(8);
} scan;
int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
struct iio_mount_matrix orientation;
u8 watermark;
};
const struct regmap_config fxls8962af_regmap_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = FXLS8962AF_MAX_REG,
};
EXPORT_SYMBOL_GPL(fxls8962af_regmap_conf);
enum {
fxls8962af_idx_x,
fxls8962af_idx_y,
fxls8962af_idx_z,
fxls8962af_idx_ts,
};
enum fxls8962af_int_pin {
FXLS8962AF_PIN_INT1,
FXLS8962AF_PIN_INT2,
};
static int fxls8962af_power_on(struct fxls8962af_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret)
dev_err(dev, "failed to power on\n");
return ret;
}
static int fxls8962af_power_off(struct fxls8962af_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev);
if (ret)
dev_err(dev, "failed to power off\n");
return ret;
}
static int fxls8962af_standby(struct fxls8962af_data *data)
{
return regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1,
FXLS8962AF_SENS_CONFIG1_ACTIVE, 0);
}
static int fxls8962af_active(struct fxls8962af_data *data)
{
return regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1,
FXLS8962AF_SENS_CONFIG1_ACTIVE, 1);
}
static int fxls8962af_is_active(struct fxls8962af_data *data)
{
unsigned int reg;
int ret;
ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG1, &reg);
if (ret)
return ret;
return reg & FXLS8962AF_SENS_CONFIG1_ACTIVE;
}
static int fxls8962af_get_out(struct fxls8962af_data *data,
struct iio_chan_spec const *chan, int *val)
{
struct device *dev = regmap_get_device(data->regmap);
__le16 raw_val;
int is_active;
int ret;
is_active = fxls8962af_is_active(data);
if (!is_active) {
ret = fxls8962af_power_on(data);
if (ret)
return ret;
}
ret = regmap_bulk_read(data->regmap, chan->address,
&raw_val, (chan->scan_type.storagebits / 8));
if (!is_active)
fxls8962af_power_off(data);
if (ret) {
dev_err(dev, "failed to get out reg 0x%lx\n", chan->address);
return ret;
}
*val = sign_extend32(le16_to_cpu(raw_val),
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
}
static int fxls8962af_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*type = IIO_VAL_INT_PLUS_NANO;
*vals = (int *)fxls8962af_scale_table;
*length = ARRAY_SIZE(fxls8962af_scale_table) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*type = IIO_VAL_INT_PLUS_MICRO;
*vals = (int *)fxls8962af_samp_freq_table;
*length = ARRAY_SIZE(fxls8962af_samp_freq_table) * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int fxls8962af_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT_PLUS_MICRO;
default:
return IIO_VAL_INT_PLUS_NANO;
}
}
static int fxls8962af_update_config(struct fxls8962af_data *data, u8 reg,
u8 mask, u8 val)
{
int ret;
int is_active;
is_active = fxls8962af_is_active(data);
if (is_active) {
ret = fxls8962af_standby(data);
if (ret)
return ret;
}
ret = regmap_update_bits(data->regmap, reg, mask, val);
if (ret)
return ret;
if (is_active) {
ret = fxls8962af_active(data);
if (ret)
return ret;
}
return 0;
}
static int fxls8962af_set_full_scale(struct fxls8962af_data *data, u32 scale)
{
int i;
for (i = 0; i < ARRAY_SIZE(fxls8962af_scale_table); i++)
if (scale == fxls8962af_scale_table[i][1])
break;
if (i == ARRAY_SIZE(fxls8962af_scale_table))
return -EINVAL;
return fxls8962af_update_config(data, FXLS8962AF_SENS_CONFIG1,
FXLS8962AF_SC1_FSR_MASK,
FXLS8962AF_SC1_FSR_PREP(i));
}
static unsigned int fxls8962af_read_full_scale(struct fxls8962af_data *data,
int *val)
{
int ret;
unsigned int reg;
u8 range_idx;
ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG1, &reg);
if (ret)
return ret;
range_idx = FXLS8962AF_SC1_FSR_GET(reg);
*val = fxls8962af_scale_table[range_idx][1];
return IIO_VAL_INT_PLUS_NANO;
}
static int fxls8962af_set_samp_freq(struct fxls8962af_data *data, u32 val,
u32 val2)
{
int i;
for (i = 0; i < ARRAY_SIZE(fxls8962af_samp_freq_table); i++)
if (val == fxls8962af_samp_freq_table[i][0] &&
val2 == fxls8962af_samp_freq_table[i][1])
break;
if (i == ARRAY_SIZE(fxls8962af_samp_freq_table))
return -EINVAL;
return fxls8962af_update_config(data, FXLS8962AF_SENS_CONFIG3,
FXLS8962AF_SC3_WAKE_ODR_MASK,
FXLS8962AF_SC3_WAKE_ODR_PREP(i));
}
static unsigned int fxls8962af_read_samp_freq(struct fxls8962af_data *data,
int *val, int *val2)
{
int ret;
unsigned int reg;
u8 range_idx;
ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG3, &reg);
if (ret)
return ret;
range_idx = FXLS8962AF_SC3_WAKE_ODR_GET(reg);
*val = fxls8962af_samp_freq_table[range_idx][0];
*val2 = fxls8962af_samp_freq_table[range_idx][1];
return IIO_VAL_INT_PLUS_MICRO;
}
static int fxls8962af_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_TEMP:
case IIO_ACCEL:
return fxls8962af_get_out(data, chan, val);
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
if (chan->type != IIO_TEMP)
return -EINVAL;
*val = FXLS8962AF_TEMP_CENTER_VAL;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
return fxls8962af_read_full_scale(data, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
return fxls8962af_read_samp_freq(data, val, val2);
default:
return -EINVAL;
}
}
static int fxls8962af_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
if (val != 0)
return -EINVAL;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = fxls8962af_set_full_scale(data, val2);
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = fxls8962af_set_samp_freq(data, val, val2);
iio_device_release_direct_mode(indio_dev);
return ret;
default:
return -EINVAL;
}
}
static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
if (val > FXLS8962AF_FIFO_LENGTH)
val = FXLS8962AF_FIFO_LENGTH;
data->watermark = val;
return 0;
}
#define FXLS8962AF_CHANNEL(axis, reg, idx) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.shift = 4, \
.endianness = IIO_BE, \
}, \
}
#define FXLS8962AF_TEMP_CHANNEL { \
.type = IIO_TEMP, \
.address = FXLS8962AF_TEMP_OUT, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_OFFSET),\
.scan_index = -1, \
.scan_type = { \
.realbits = 8, \
.storagebits = 8, \
}, \
}
static const struct iio_chan_spec fxls8962af_channels[] = {
FXLS8962AF_CHANNEL(X, FXLS8962AF_OUT_X_LSB, fxls8962af_idx_x),
FXLS8962AF_CHANNEL(Y, FXLS8962AF_OUT_Y_LSB, fxls8962af_idx_y),
FXLS8962AF_CHANNEL(Z, FXLS8962AF_OUT_Z_LSB, fxls8962af_idx_z),
IIO_CHAN_SOFT_TIMESTAMP(fxls8962af_idx_ts),
FXLS8962AF_TEMP_CHANNEL,
};
static const struct fxls8962af_chip_info fxls_chip_info_table[] = {
[fxls8962af] = {
.chip_id = FXLS8962AF_DEVICE_ID,
.name = "fxls8962af",
.channels = fxls8962af_channels,
.num_channels = ARRAY_SIZE(fxls8962af_channels),
},
[fxls8964af] = {
.chip_id = FXLS8964AF_DEVICE_ID,
.name = "fxls8964af",
.channels = fxls8962af_channels,
.num_channels = ARRAY_SIZE(fxls8962af_channels),
},
};
static const struct iio_info fxls8962af_info = {
.read_raw = &fxls8962af_read_raw,
.write_raw = &fxls8962af_write_raw,
.write_raw_get_fmt = fxls8962af_write_raw_get_fmt,
.read_avail = fxls8962af_read_avail,
.hwfifo_set_watermark = fxls8962af_set_watermark,
};
static int fxls8962af_reset(struct fxls8962af_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
unsigned int reg;
int ret;
ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1,
FXLS8962AF_SENS_CONFIG1_RST,
FXLS8962AF_SENS_CONFIG1_RST);
if (ret)
return ret;
/* TBOOT1, TBOOT2, specifies we have to wait between 1 - 17.7ms */
ret = regmap_read_poll_timeout(data->regmap, FXLS8962AF_INT_STATUS, reg,
(reg & FXLS8962AF_INT_STATUS_SRC_BOOT),
1000, 18000);
if (ret == -ETIMEDOUT)
dev_err(dev, "reset timeout, int_status = 0x%x\n", reg);
return ret;
}
static int __fxls8962af_fifo_set_mode(struct fxls8962af_data *data, bool onoff)
{
int ret;
/* Enable watermark at max fifo size */
ret = regmap_update_bits(data->regmap, FXLS8962AF_BUF_CONFIG2,
FXLS8962AF_BUF_CONFIG2_BUF_WMRK,
data->watermark);
if (ret)
return ret;
return regmap_update_bits(data->regmap, FXLS8962AF_BUF_CONFIG1,
FXLS8962AF_BC1_BUF_MODE_MASK,
FXLS8962AF_BC1_BUF_MODE_PREP(onoff));
}
static int fxls8962af_buffer_preenable(struct iio_dev *indio_dev)
{
return fxls8962af_power_on(iio_priv(indio_dev));
}
static int fxls8962af_buffer_postenable(struct iio_dev *indio_dev)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
int ret;
fxls8962af_standby(data);
/* Enable buffer interrupt */
ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_EN,
FXLS8962AF_INT_EN_BUF_EN,
FXLS8962AF_INT_EN_BUF_EN);
if (ret)
return ret;
ret = __fxls8962af_fifo_set_mode(data, true);
fxls8962af_active(data);
return ret;
}
static int fxls8962af_buffer_predisable(struct iio_dev *indio_dev)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
int ret;
fxls8962af_standby(data);
/* Disable buffer interrupt */
ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_EN,
FXLS8962AF_INT_EN_BUF_EN, 0);
if (ret)
return ret;
ret = __fxls8962af_fifo_set_mode(data, false);
fxls8962af_active(data);
return ret;
}
static int fxls8962af_buffer_postdisable(struct iio_dev *indio_dev)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
return fxls8962af_power_off(data);
}
static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = {
.preenable = fxls8962af_buffer_preenable,
.postenable = fxls8962af_buffer_postenable,
.predisable = fxls8962af_buffer_predisable,
.postdisable = fxls8962af_buffer_postdisable,
};
static int fxls8962af_i2c_raw_read_errata3(struct fxls8962af_data *data,
u16 *buffer, int samples,
int sample_length)
{
int i, ret;
for (i = 0; i < samples; i++) {
ret = regmap_raw_read(data->regmap, FXLS8962AF_BUF_X_LSB,
&buffer[i * 3], sample_length);
if (ret)
return ret;
}
return ret;
}
static int fxls8962af_fifo_transfer(struct fxls8962af_data *data,
u16 *buffer, int samples)
{
struct device *dev = regmap_get_device(data->regmap);
int sample_length = 3 * sizeof(*buffer);
int total_length = samples * sample_length;
int ret;
if (i2c_verify_client(dev))
/*
* Due to errata bug:
* E3: FIFO burst read operation error using I2C interface
* We have to avoid burst reads on I2C..
*/
ret = fxls8962af_i2c_raw_read_errata3(data, buffer, samples,
sample_length);
else
ret = regmap_raw_read(data->regmap, FXLS8962AF_BUF_X_LSB, buffer,
total_length);
if (ret)
dev_err(dev, "Error transferring data from fifo: %d\n", ret);
return ret;
}
static int fxls8962af_fifo_flush(struct iio_dev *indio_dev)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
struct device *dev = regmap_get_device(data->regmap);
u16 buffer[FXLS8962AF_FIFO_LENGTH * 3];
uint64_t sample_period;
unsigned int reg;
int64_t tstamp;
int ret, i;
u8 count;
ret = regmap_read(data->regmap, FXLS8962AF_BUF_STATUS, &reg);
if (ret)
return ret;
if (reg & FXLS8962AF_BUF_STATUS_BUF_OVF) {
dev_err(dev, "Buffer overflow");
return -EOVERFLOW;
}
count = reg & FXLS8962AF_BUF_STATUS_BUF_CNT;
if (!count)
return 0;
data->old_timestamp = data->timestamp;
data->timestamp = iio_get_time_ns(indio_dev);
/*
* Approximate timestamps for each of the sample based on the sampling,
* frequency, timestamp for last sample and number of samples.
*/
sample_period = (data->timestamp - data->old_timestamp);
do_div(sample_period, count);
tstamp = data->timestamp - (count - 1) * sample_period;
ret = fxls8962af_fifo_transfer(data, buffer, count);
if (ret)
return ret;
/* Demux hw FIFO into kfifo. */
for (i = 0; i < count; i++) {
int j, bit;
j = 0;
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit],
sizeof(data->scan.channels[0]));
}
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
tstamp);
tstamp += sample_period;
}
return count;
}
static irqreturn_t fxls8962af_interrupt(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct fxls8962af_data *data = iio_priv(indio_dev);
unsigned int reg;
int ret;
ret = regmap_read(data->regmap, FXLS8962AF_INT_STATUS, &reg);
if (ret)
return IRQ_NONE;
if (reg & FXLS8962AF_INT_STATUS_SRC_BUF) {
ret = fxls8962af_fifo_flush(indio_dev);
if (ret)
return IRQ_NONE;
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static void fxls8962af_regulator_disable(void *data_ptr)
{
struct fxls8962af_data *data = data_ptr;
regulator_disable(data->vdd_reg);
}
static void fxls8962af_pm_disable(void *dev_ptr)
{
struct device *dev = dev_ptr;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
fxls8962af_standby(iio_priv(indio_dev));
}
static void fxls8962af_get_irq(struct device_node *of_node,
enum fxls8962af_int_pin *pin)
{
int irq;
irq = of_irq_get_byname(of_node, "INT2");
if (irq > 0) {
*pin = FXLS8962AF_PIN_INT2;
return;
}
*pin = FXLS8962AF_PIN_INT1;
}
static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
struct device *dev = regmap_get_device(data->regmap);
unsigned long irq_type;
bool irq_active_high;
enum fxls8962af_int_pin int_pin;
u8 int_pin_sel;
int ret;
fxls8962af_get_irq(dev->of_node, &int_pin);
switch (int_pin) {
case FXLS8962AF_PIN_INT1:
int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1;
break;
case FXLS8962AF_PIN_INT2:
int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT2;
break;
default:
dev_err(dev, "unsupported int pin selected\n");
return -EINVAL;
}
ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_PIN_SEL,
FXLS8962AF_INT_PIN_SEL_MASK, int_pin_sel);
if (ret)
return ret;
irq_type = irqd_get_trigger_type(irq_get_irq_data(irq));
switch (irq_type) {
case IRQF_TRIGGER_HIGH:
case IRQF_TRIGGER_RISING:
irq_active_high = true;
break;
case IRQF_TRIGGER_LOW:
case IRQF_TRIGGER_FALLING:
irq_active_high = false;
break;
default:
dev_info(dev, "mode %lx unsupported\n", irq_type);
return -EINVAL;
}
ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG4,
FXLS8962AF_SC4_INT_POL_MASK,
FXLS8962AF_SC4_INT_POL_PREP(irq_active_high));
if (ret)
return ret;
if (device_property_read_bool(dev, "drive-open-drain")) {
ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG4,
FXLS8962AF_SC4_INT_PP_OD_MASK,
FXLS8962AF_SC4_INT_PP_OD_PREP(1));
if (ret)
return ret;
irq_type |= IRQF_SHARED;
}
return devm_request_threaded_irq(dev,
irq,
NULL, fxls8962af_interrupt,
irq_type | IRQF_ONESHOT,
indio_dev->name, indio_dev);
}
int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq)
{
struct fxls8962af_data *data;
struct iio_dev *indio_dev;
unsigned int reg;
int ret, i;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
return ret;
data->vdd_reg = devm_regulator_get(dev, "vdd");
if (IS_ERR(data->vdd_reg))
return dev_err_probe(dev, PTR_ERR(data->vdd_reg),
"Failed to get vdd regulator\n");
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(dev, "Failed to enable vdd regulator: %d\n", ret);
return ret;
}
ret = devm_add_action_or_reset(dev, fxls8962af_regulator_disable, data);
if (ret)
return ret;
ret = regmap_read(data->regmap, FXLS8962AF_WHO_AM_I, &reg);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(fxls_chip_info_table); i++) {
if (fxls_chip_info_table[i].chip_id == reg) {
data->chip_info = &fxls_chip_info_table[i];
break;
}
}
if (i == ARRAY_SIZE(fxls_chip_info_table)) {
dev_err(dev, "failed to match device in table\n");
return -ENXIO;
}
indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->num_channels;
indio_dev->name = data->chip_info->name;
indio_dev->info = &fxls8962af_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = fxls8962af_reset(data);
if (ret)
return ret;
if (irq) {
ret = fxls8962af_irq_setup(indio_dev, irq);
if (ret)
return ret;
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
INDIO_BUFFER_SOFTWARE,
&fxls8962af_buffer_ops);
if (ret)
return ret;
}
ret = pm_runtime_set_active(dev);
if (ret)
return ret;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, FXLS8962AF_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
ret = devm_add_action_or_reset(dev, fxls8962af_pm_disable, dev);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(fxls8962af_core_probe);
static int __maybe_unused fxls8962af_runtime_suspend(struct device *dev)
{
struct fxls8962af_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
ret = fxls8962af_standby(data);
if (ret) {
dev_err(dev, "powering off device failed\n");
return ret;
}
return 0;
}
static int __maybe_unused fxls8962af_runtime_resume(struct device *dev)
{
struct fxls8962af_data *data = iio_priv(dev_get_drvdata(dev));
return fxls8962af_active(data);
}
const struct dev_pm_ops fxls8962af_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend,
fxls8962af_runtime_resume, NULL)
};
EXPORT_SYMBOL_GPL(fxls8962af_pm_ops);
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>");
MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver
*
* Copyright 2021 Connected Cars A/S
*/
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "fxls8962af.h"
static int fxls8962af_probe(struct i2c_client *client)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &fxls8962af_regmap_conf);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to initialize i2c regmap\n");
return PTR_ERR(regmap);
}
return fxls8962af_core_probe(&client->dev, regmap, client->irq);
}
static const struct i2c_device_id fxls8962af_id[] = {
{ "fxls8962af", fxls8962af },
{ "fxls8964af", fxls8964af },
{}
};
MODULE_DEVICE_TABLE(i2c, fxls8962af_id);
static const struct of_device_id fxls8962af_of_match[] = {
{ .compatible = "nxp,fxls8962af" },
{ .compatible = "nxp,fxls8964af" },
{}
};
MODULE_DEVICE_TABLE(of, fxls8962af_of_match);
static struct i2c_driver fxls8962af_driver = {
.driver = {
.name = "fxls8962af_i2c",
.of_match_table = fxls8962af_of_match,
.pm = &fxls8962af_pm_ops,
},
.probe_new = fxls8962af_probe,
.id_table = fxls8962af_id,
};
module_i2c_driver(fxls8962af_driver);
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>");
MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer i2c driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NXP FXLS8962AF/FXLS8964AF Accelerometer SPI Driver
*
* Copyright 2021 Connected Cars A/S
*/
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include "fxls8962af.h"
static int fxls8962af_probe(struct spi_device *spi)
{
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &fxls8962af_regmap_conf);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to initialize spi regmap\n");
return PTR_ERR(regmap);
}
return fxls8962af_core_probe(&spi->dev, regmap, spi->irq);
}
static const struct of_device_id fxls8962af_spi_of_match[] = {
{ .compatible = "nxp,fxls8962af" },
{ .compatible = "nxp,fxls8964af" },
{}
};
MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match);
static const struct spi_device_id fxls8962af_spi_id_table[] = {
{ "fxls8962af", fxls8962af },
{ "fxls8964af", fxls8964af },
{}
};
MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table);
static struct spi_driver fxls8962af_driver = {
.driver = {
.name = "fxls8962af_spi",
.pm = &fxls8962af_pm_ops,
.of_match_table = fxls8962af_spi_of_match,
},
.probe = fxls8962af_probe,
.id_table = fxls8962af_spi_id_table,
};
module_spi_driver(fxls8962af_driver);
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>");
MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer spi driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2021 Connected Cars A/S
*/
#ifndef _FXLS8962AF_H_
#define _FXLS8962AF_H_
struct regmap;
struct device;
enum {
fxls8962af,
fxls8964af,
};
int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq);
int fxls8962af_core_remove(struct device *dev);
extern const struct dev_pm_ops fxls8962af_pm_ops;
extern const struct regmap_config fxls8962af_regmap_conf;
#endif /* _FXLS8962AF_H_ */

View File

@ -28,8 +28,11 @@ struct accel_3d_state {
struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes; struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
/* Reserve for 3 channels + padding + timestamp */ /* Ensure timestamp is naturally aligned */
u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3]; struct {
u32 accel_val[3];
s64 timestamp __aligned(8);
} scan;
int scale_pre_decml; int scale_pre_decml;
int scale_post_decml; int scale_post_decml;
int scale_precision; int scale_precision;
@ -245,8 +248,8 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
accel_state->timestamp = iio_get_time_ns(indio_dev); accel_state->timestamp = iio_get_time_ns(indio_dev);
hid_sensor_push_data(indio_dev, hid_sensor_push_data(indio_dev,
accel_state->accel_val, &accel_state->scan,
sizeof(accel_state->accel_val), sizeof(accel_state->scan),
accel_state->timestamp); accel_state->timestamp);
accel_state->timestamp = 0; accel_state->timestamp = 0;
@ -271,7 +274,7 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
case HID_USAGE_SENSOR_ACCEL_Y_AXIS: case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
case HID_USAGE_SENSOR_ACCEL_Z_AXIS: case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS; offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] = accel_state->scan.accel_val[CHANNEL_SCAN_INDEX_X + offset] =
*(u32 *)raw_data; *(u32 *)raw_data;
ret = 0; ret = 0;
break; break;

View File

@ -51,13 +51,15 @@
#define KXTF9_REG_TILT_POS_CUR 0x10 #define KXTF9_REG_TILT_POS_CUR 0x10
#define KXTF9_REG_TILT_POS_PREV 0x11 #define KXTF9_REG_TILT_POS_PREV 0x11
#define KXTF9_REG_INT_SRC1 0x15 #define KXTF9_REG_INT_SRC1 0x15
#define KXCJK1013_REG_INT_SRC1 0x16 /* compatible, but called INT_SRC2 in KXTF9 ds */ #define KXTF9_REG_INT_SRC2 0x16
#define KXCJK1013_REG_INT_SRC1 0x16
#define KXCJK1013_REG_INT_SRC2 0x17 #define KXCJK1013_REG_INT_SRC2 0x17
#define KXCJK1013_REG_STATUS_REG 0x18 #define KXCJK1013_REG_STATUS_REG 0x18
#define KXCJK1013_REG_INT_REL 0x1A #define KXCJK1013_REG_INT_REL 0x1A
#define KXCJK1013_REG_CTRL1 0x1B #define KXCJK1013_REG_CTRL1 0x1B
#define KXTF9_REG_CTRL2 0x1C #define KXTF9_REG_CTRL2 0x1C
#define KXCJK1013_REG_CTRL2 0x1D /* mostly compatible, CTRL_REG3 in KTXF9 ds */ #define KXTF9_REG_CTRL3 0x1D
#define KXCJK1013_REG_CTRL2 0x1D
#define KXCJK1013_REG_INT_CTRL1 0x1E #define KXCJK1013_REG_INT_CTRL1 0x1E
#define KXCJK1013_REG_INT_CTRL2 0x1F #define KXCJK1013_REG_INT_CTRL2 0x1F
#define KXTF9_REG_INT_CTRL3 0x20 #define KXTF9_REG_INT_CTRL3 0x20
@ -77,6 +79,45 @@
#define KXTF9_REG_HYST_SET 0x5F #define KXTF9_REG_HYST_SET 0x5F
#define KXCJK1013_REG_WAKE_THRES 0x6A #define KXCJK1013_REG_WAKE_THRES 0x6A
/* Everything up to 0x11 is equal to KXCJK1013/KXTF9 above */
#define KX023_REG_INS1 0x12
#define KX023_REG_INS2 0x13
#define KX023_REG_INS3 0x14
#define KX023_REG_STAT 0x15
#define KX023_REG_INT_REL 0x17
#define KX023_REG_CNTL1 0x18
#define KX023_REG_CNTL2 0x19
#define KX023_REG_CNTL3 0x1A
#define KX023_REG_ODCNTL 0x1B
#define KX023_REG_INC1 0x1C
#define KX023_REG_INC2 0x1D
#define KX023_REG_INC3 0x1E
#define KX023_REG_INC4 0x1F
#define KX023_REG_INC5 0x20
#define KX023_REG_INC6 0x21
#define KX023_REG_TILT_TIMER 0x22
#define KX023_REG_WUFC 0x23
#define KX023_REG_TDTRC 0x24
#define KX023_REG_TDTC 0x25
#define KX023_REG_TTH 0x26
#define KX023_REG_TTL 0x27
#define KX023_REG_FTD 0x28
#define KX023_REG_STD 0x29
#define KX023_REG_TLT 0x2A
#define KX023_REG_TWS 0x2B
#define KX023_REG_ATH 0x30
#define KX023_REG_TILT_ANGLE_LL 0x32
#define KX023_REG_TILT_ANGLE_HL 0x33
#define KX023_REG_HYST_SET 0x34
#define KX023_REG_LP_CNTL 0x35
#define KX023_REG_BUF_CNTL1 0x3A
#define KX023_REG_BUF_CNTL2 0x3B
#define KX023_REG_BUF_STATUS_1 0x3C
#define KX023_REG_BUF_STATUS_2 0x3D
#define KX023_REG_BUF_CLEAR 0x3E
#define KX023_REG_BUF_READ 0x3F
#define KX023_REG_SELF_TEST 0x60
#define KXCJK1013_REG_CTRL1_BIT_PC1 BIT(7) #define KXCJK1013_REG_CTRL1_BIT_PC1 BIT(7)
#define KXCJK1013_REG_CTRL1_BIT_RES BIT(6) #define KXCJK1013_REG_CTRL1_BIT_RES BIT(6)
#define KXCJK1013_REG_CTRL1_BIT_DRDY BIT(5) #define KXCJK1013_REG_CTRL1_BIT_DRDY BIT(5)
@ -117,6 +158,14 @@
#define KXCJK1013_REG_INT_SRC2_BIT_XP BIT(4) #define KXCJK1013_REG_INT_SRC2_BIT_XP BIT(4)
#define KXCJK1013_REG_INT_SRC2_BIT_XN BIT(5) #define KXCJK1013_REG_INT_SRC2_BIT_XN BIT(5)
/* KX023 interrupt routing to INT1. INT2 can be configured with INC6 */
#define KX023_REG_INC4_BFI1 BIT(6)
#define KX023_REG_INC4_WMI1 BIT(5)
#define KX023_REG_INC4_DRDY1 BIT(4)
#define KX023_REG_INC4_TDTI1 BIT(2)
#define KX023_REG_INC4_WUFI1 BIT(1)
#define KX023_REG_INC4_TPI1 BIT(0)
#define KXCJK1013_DEFAULT_WAKE_THRES 1 #define KXCJK1013_DEFAULT_WAKE_THRES 1
enum kx_chipset { enum kx_chipset {
@ -124,6 +173,7 @@ enum kx_chipset {
KXCJ91008, KXCJ91008,
KXTJ21009, KXTJ21009,
KXTF9, KXTF9,
KX0231025,
KX_MAX_CHIPS /* this must be last */ KX_MAX_CHIPS /* this must be last */
}; };
@ -133,6 +183,63 @@ enum kx_acpi_type {
ACPI_KIOX010A, ACPI_KIOX010A,
}; };
struct kx_chipset_regs {
u8 int_src1;
u8 int_src2;
u8 int_rel;
u8 ctrl1;
u8 wuf_ctrl;
u8 int_ctrl1;
u8 data_ctrl;
u8 wake_timer;
u8 wake_thres;
};
static const struct kx_chipset_regs kxcjk1013_regs = {
.int_src1 = KXCJK1013_REG_INT_SRC1,
.int_src2 = KXCJK1013_REG_INT_SRC2,
.int_rel = KXCJK1013_REG_INT_REL,
.ctrl1 = KXCJK1013_REG_CTRL1,
.wuf_ctrl = KXCJK1013_REG_CTRL2,
.int_ctrl1 = KXCJK1013_REG_INT_CTRL1,
.data_ctrl = KXCJK1013_REG_DATA_CTRL,
.wake_timer = KXCJK1013_REG_WAKE_TIMER,
.wake_thres = KXCJK1013_REG_WAKE_THRES,
};
static const struct kx_chipset_regs kxtf9_regs = {
/* .int_src1 was moved to INT_SRC2 on KXTF9 */
.int_src1 = KXTF9_REG_INT_SRC2,
/* .int_src2 is not available */
.int_rel = KXCJK1013_REG_INT_REL,
.ctrl1 = KXCJK1013_REG_CTRL1,
.wuf_ctrl = KXTF9_REG_CTRL3,
.int_ctrl1 = KXCJK1013_REG_INT_CTRL1,
.data_ctrl = KXCJK1013_REG_DATA_CTRL,
.wake_timer = KXCJK1013_REG_WAKE_TIMER,
.wake_thres = KXTF9_REG_WAKE_THRESH,
};
/* The registers have totally different names but the bits are compatible */
static const struct kx_chipset_regs kx0231025_regs = {
.int_src1 = KX023_REG_INS2,
.int_src2 = KX023_REG_INS3,
.int_rel = KX023_REG_INT_REL,
.ctrl1 = KX023_REG_CNTL1,
.wuf_ctrl = KX023_REG_CNTL3,
.int_ctrl1 = KX023_REG_INC1,
.data_ctrl = KX023_REG_ODCNTL,
.wake_timer = KX023_REG_WUFC,
.wake_thres = KX023_REG_ATH,
};
enum kxcjk1013_axis {
AXIS_X,
AXIS_Y,
AXIS_Z,
AXIS_MAX
};
struct kxcjk1013_data { struct kxcjk1013_data {
struct regulator_bulk_data regulators[2]; struct regulator_bulk_data regulators[2];
struct i2c_client *client; struct i2c_client *client;
@ -140,7 +247,11 @@ struct kxcjk1013_data {
struct iio_trigger *motion_trig; struct iio_trigger *motion_trig;
struct iio_mount_matrix orientation; struct iio_mount_matrix orientation;
struct mutex mutex; struct mutex mutex;
s16 buffer[8]; /* Ensure timestamp naturally aligned */
struct {
s16 chans[AXIS_MAX];
s64 timestamp __aligned(8);
} scan;
u8 odr_bits; u8 odr_bits;
u8 range; u8 range;
int wake_thres; int wake_thres;
@ -152,13 +263,7 @@ struct kxcjk1013_data {
int64_t timestamp; int64_t timestamp;
enum kx_chipset chipset; enum kx_chipset chipset;
enum kx_acpi_type acpi_type; enum kx_acpi_type acpi_type;
}; const struct kx_chipset_regs *regs;
enum kxcjk1013_axis {
AXIS_X,
AXIS_Y,
AXIS_Z,
AXIS_MAX,
}; };
enum kxcjk1013_mode { enum kxcjk1013_mode {
@ -268,6 +373,22 @@ static const struct {
{0x05, 5100}, {0x05, 5100},
{0x06, 2700}, {0x06, 2700},
}, },
/* KX023-1025 */
{
/* First 4 are not in datasheet, taken from KXCTJ2-1009 */
{0x08, 1240000},
{0x09, 621000},
{0x0A, 309000},
{0x0B, 151000},
{0, 81000},
{0x01, 40000},
{0x02, 22000},
{0x03, 12000},
{0x04, 7000},
{0x05, 4400},
{0x06, 3000},
{0x07, 3000},
},
}; };
static const struct { static const struct {
@ -309,7 +430,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
{ {
int ret; int ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
return ret; return ret;
@ -320,8 +441,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
else else
ret |= KXCJK1013_REG_CTRL1_BIT_PC1; ret |= KXCJK1013_REG_CTRL1_BIT_PC1;
ret = i2c_smbus_write_byte_data(data->client, ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
KXCJK1013_REG_CTRL1, ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
return ret; return ret;
@ -335,7 +455,7 @@ static int kxcjk1013_get_mode(struct kxcjk1013_data *data,
{ {
int ret; int ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
return ret; return ret;
@ -353,7 +473,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index)
{ {
int ret; int ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
return ret; return ret;
@ -364,9 +484,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index)
ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3);
ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4);
ret = i2c_smbus_write_byte_data(data->client, ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
KXCJK1013_REG_CTRL1,
ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
return ret; return ret;
@ -400,7 +518,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
return ret; return ret;
@ -409,8 +527,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
/* Set 12 bit mode */ /* Set 12 bit mode */
ret |= KXCJK1013_REG_CTRL1_BIT_RES; ret |= KXCJK1013_REG_CTRL1_BIT_RES;
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL1, ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl\n"); dev_err(&data->client->dev, "Error reading reg_ctrl\n");
return ret; return ret;
@ -421,7 +538,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_DATA_CTRL); ret = i2c_smbus_read_byte_data(data->client, data->regs->data_ctrl);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_data_ctrl\n"); dev_err(&data->client->dev, "Error reading reg_data_ctrl\n");
return ret; return ret;
@ -430,7 +547,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
data->odr_bits = ret; data->odr_bits = ret;
/* Set up INT polarity */ /* Set up INT polarity */
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
return ret; return ret;
@ -441,13 +558,23 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
else else
ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEA; ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEA;
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret);
ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
return ret; return ret;
} }
/* On KX023, route all used interrupts to INT1 for now */
if (data->chipset == KX0231025 && data->client->irq > 0) {
ret = i2c_smbus_write_byte_data(data->client, KX023_REG_INC4,
KX023_REG_INC4_DRDY1 |
KX023_REG_INC4_WUFI1);
if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_inc4\n");
return ret;
}
}
ret = kxcjk1013_set_mode(data, OPERATION); ret = kxcjk1013_set_mode(data, OPERATION);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -478,7 +605,7 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
int ret; int ret;
if (on) if (on)
ret = pm_runtime_get_sync(&data->client->dev); ret = pm_runtime_resume_and_get(&data->client->dev);
else { else {
pm_runtime_mark_last_busy(&data->client->dev); pm_runtime_mark_last_busy(&data->client->dev);
ret = pm_runtime_put_autosuspend(&data->client->dev); ret = pm_runtime_put_autosuspend(&data->client->dev);
@ -486,8 +613,6 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, dev_err(&data->client->dev,
"Failed: %s for %d\n", __func__, on); "Failed: %s for %d\n", __func__, on);
if (on)
pm_runtime_put_noidle(&data->client->dev);
return ret; return ret;
} }
#endif #endif
@ -497,10 +622,9 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data)
{ {
int waketh_reg, ret; int ret;
ret = i2c_smbus_write_byte_data(data->client, ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_timer,
KXCJK1013_REG_WAKE_TIMER,
data->wake_dur); data->wake_dur);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, dev_err(&data->client->dev,
@ -508,9 +632,7 @@ static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data)
return ret; return ret;
} }
waketh_reg = data->chipset == KXTF9 ? ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_thres,
KXTF9_REG_WAKE_THRESH : KXCJK1013_REG_WAKE_THRES;
ret = i2c_smbus_write_byte_data(data->client, waketh_reg,
data->wake_thres); data->wake_thres);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_wake_thres\n"); dev_err(&data->client->dev, "Error writing reg_wake_thres\n");
@ -539,7 +661,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
return ret; return ret;
@ -550,14 +672,13 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data,
else else
ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN;
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret);
ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
return ret; return ret;
} }
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
return ret; return ret;
@ -568,8 +689,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data,
else else
ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE; ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE;
ret = i2c_smbus_write_byte_data(data->client, ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
KXCJK1013_REG_CTRL1, ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
return ret; return ret;
@ -599,7 +719,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
return ret; return ret;
@ -610,14 +730,13 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
else else
ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN;
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret);
ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
return ret; return ret;
} }
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
return ret; return ret;
@ -628,8 +747,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
else else
ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY;
ret = i2c_smbus_write_byte_data(data->client, ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
KXCJK1013_REG_CTRL1, ret);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
return ret; return ret;
@ -701,7 +819,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_DATA_CTRL, ret = i2c_smbus_write_byte_data(data->client, data->regs->data_ctrl,
odr_setting->odr_bits); odr_setting->odr_bits);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing data_ctrl\n"); dev_err(&data->client->dev, "Error writing data_ctrl\n");
@ -710,7 +828,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
data->odr_bits = odr_setting->odr_bits; data->odr_bits = odr_setting->odr_bits;
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL2, ret = i2c_smbus_write_byte_data(data->client, data->regs->wuf_ctrl,
odr_setting->wuf_bits); odr_setting->wuf_bits);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_ctrl2\n"); dev_err(&data->client->dev, "Error writing reg_ctrl2\n");
@ -1094,12 +1212,12 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p)
ret = i2c_smbus_read_i2c_block_data_or_emulated(data->client, ret = i2c_smbus_read_i2c_block_data_or_emulated(data->client,
KXCJK1013_REG_XOUT_L, KXCJK1013_REG_XOUT_L,
AXIS_MAX * 2, AXIS_MAX * 2,
(u8 *)data->buffer); (u8 *)data->scan.chans);
mutex_unlock(&data->mutex); mutex_unlock(&data->mutex);
if (ret < 0) if (ret < 0)
goto err; goto err;
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
data->timestamp); data->timestamp);
err: err:
iio_trigger_notify_done(indio_dev->trig); iio_trigger_notify_done(indio_dev->trig);
@ -1113,7 +1231,7 @@ static void kxcjk1013_trig_reen(struct iio_trigger *trig)
struct kxcjk1013_data *data = iio_priv(indio_dev); struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret; int ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel);
if (ret < 0) if (ret < 0)
dev_err(&data->client->dev, "Error reading reg_int_rel\n"); dev_err(&data->client->dev, "Error reading reg_int_rel\n");
} }
@ -1166,8 +1284,7 @@ static void kxcjk1013_report_motion_event(struct iio_dev *indio_dev)
{ {
struct kxcjk1013_data *data = iio_priv(indio_dev); struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret = i2c_smbus_read_byte_data(data->client, int ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src2);
KXCJK1013_REG_INT_SRC2);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_int_src2\n"); dev_err(&data->client->dev, "Error reading reg_int_src2\n");
return; return;
@ -1234,7 +1351,7 @@ static irqreturn_t kxcjk1013_event_handler(int irq, void *private)
struct kxcjk1013_data *data = iio_priv(indio_dev); struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret; int ret;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_SRC1); ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src1);
if (ret < 0) { if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_int_src1\n"); dev_err(&data->client->dev, "Error reading reg_int_src1\n");
goto ack_intr; goto ack_intr;
@ -1257,7 +1374,7 @@ ack_intr:
if (data->dready_trigger_on) if (data->dready_trigger_on)
return IRQ_HANDLED; return IRQ_HANDLED;
ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel);
if (ret < 0) if (ret < 0)
dev_err(&data->client->dev, "Error reading reg_int_rel\n"); dev_err(&data->client->dev, "Error reading reg_int_rel\n");
@ -1338,8 +1455,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
} else { } else {
data->active_high_intr = true; /* default polarity */ data->active_high_intr = true; /* default polarity */
ret = iio_read_mount_matrix(&client->dev, "mount-matrix", ret = iio_read_mount_matrix(&client->dev, &data->orientation);
&data->orientation);
if (ret) if (ret)
return ret; return ret;
} }
@ -1378,6 +1494,22 @@ static int kxcjk1013_probe(struct i2c_client *client,
} else } else
return -ENODEV; return -ENODEV;
switch (data->chipset) {
case KXCJK1013:
case KXCJ91008:
case KXTJ21009:
data->regs = &kxcjk1013_regs;
break;
case KXTF9:
data->regs = &kxtf9_regs;
break;
case KX0231025:
data->regs = &kx0231025_regs;
break;
default:
return -EINVAL;
}
ret = kxcjk1013_chip_init(data); ret = kxcjk1013_chip_init(data);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1404,7 +1536,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
data->dready_trig = devm_iio_trigger_alloc(&client->dev, data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d", "%s-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!data->dready_trig) { if (!data->dready_trig) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_poweroff; goto err_poweroff;
@ -1413,7 +1545,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
data->motion_trig = devm_iio_trigger_alloc(&client->dev, data->motion_trig = devm_iio_trigger_alloc(&client->dev,
"%s-any-motion-dev%d", "%s-any-motion-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!data->motion_trig) { if (!data->motion_trig) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_poweroff; goto err_poweroff;
@ -1485,7 +1617,6 @@ static int kxcjk1013_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev); pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev); pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
if (data->dready_trig) { if (data->dready_trig) {
iio_triggered_buffer_cleanup(indio_dev); iio_triggered_buffer_cleanup(indio_dev);
@ -1593,6 +1724,7 @@ static const struct i2c_device_id kxcjk1013_id[] = {
{"kxcj91008", KXCJ91008}, {"kxcj91008", KXCJ91008},
{"kxtj21009", KXTJ21009}, {"kxtj21009", KXTJ21009},
{"kxtf9", KXTF9}, {"kxtf9", KXTF9},
{"kx023-1025", KX0231025},
{"SMO8500", KXCJ91008}, {"SMO8500", KXCJ91008},
{} {}
}; };
@ -1604,6 +1736,7 @@ static const struct of_device_id kxcjk1013_of_match[] = {
{ .compatible = "kionix,kxcj91008", }, { .compatible = "kionix,kxcj91008", },
{ .compatible = "kionix,kxtj21009", }, { .compatible = "kionix,kxtj21009", },
{ .compatible = "kionix,kxtf9", }, { .compatible = "kionix,kxtf9", },
{ .compatible = "kionix,kx023-1025", },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, kxcjk1013_of_match); MODULE_DEVICE_TABLE(of, kxcjk1013_of_match);

View File

@ -420,7 +420,7 @@ int kxsd9_common_probe(struct device *dev,
indio_dev->available_scan_masks = kxsd9_scan_masks; indio_dev->available_scan_masks = kxsd9_scan_masks;
/* Read the mounting matrix, if present */ /* Read the mounting matrix, if present */
ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation); ret = iio_read_mount_matrix(dev, &st->orientation);
if (ret) if (ret)
return ret; return ret;

View File

@ -221,7 +221,7 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
int ret; int ret;
if (on) { if (on) {
ret = pm_runtime_get_sync(&client->dev); ret = pm_runtime_resume_and_get(&client->dev);
} else { } else {
pm_runtime_mark_last_busy(&client->dev); pm_runtime_mark_last_busy(&client->dev);
ret = pm_runtime_put_autosuspend(&client->dev); ret = pm_runtime_put_autosuspend(&client->dev);
@ -230,8 +230,6 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, dev_err(&client->dev,
"failed to change power state to %d\n", on); "failed to change power state to %d\n", on);
if (on)
pm_runtime_put_noidle(&client->dev);
return ret; return ret;
} }
@ -1461,7 +1459,7 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev)
trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d", trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!trig) if (!trig)
return -ENOMEM; return -ENOMEM;
@ -1711,7 +1709,6 @@ static int mma8452_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev); pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev); pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_triggered_buffer_cleanup(indio_dev); iio_triggered_buffer_cleanup(indio_dev);
mma8452_trigger_cleanup(indio_dev); mma8452_trigger_cleanup(indio_dev);

View File

@ -515,7 +515,6 @@ static int mma9551_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev); pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev); pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
mutex_lock(&data->mutex); mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false); mma9551_set_device_state(data->client, false);

View File

@ -664,7 +664,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on)
int ret; int ret;
if (on) if (on)
ret = pm_runtime_get_sync(&client->dev); ret = pm_runtime_resume_and_get(&client->dev);
else { else {
pm_runtime_mark_last_busy(&client->dev); pm_runtime_mark_last_busy(&client->dev);
ret = pm_runtime_put_autosuspend(&client->dev); ret = pm_runtime_put_autosuspend(&client->dev);
@ -673,8 +673,6 @@ int mma9551_set_power_state(struct i2c_client *client, bool on)
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, dev_err(&client->dev,
"failed to change power state to %d\n", on); "failed to change power state to %d\n", on);
if (on)
pm_runtime_put_noidle(&client->dev);
return ret; return ret;
} }

View File

@ -1154,7 +1154,6 @@ static int mma9553_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev); pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev); pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
mutex_lock(&data->mutex); mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false); mma9551_set_device_state(data->client, false);

View File

@ -56,7 +56,11 @@ struct mxc4005_data {
struct mutex mutex; struct mutex mutex;
struct regmap *regmap; struct regmap *regmap;
struct iio_trigger *dready_trig; struct iio_trigger *dready_trig;
__be16 buffer[8]; /* Ensure timestamp is naturally aligned */
struct {
__be16 chans[3];
s64 timestamp __aligned(8);
} scan;
bool trigger_enabled; bool trigger_enabled;
}; };
@ -135,7 +139,7 @@ static int mxc4005_read_xyz(struct mxc4005_data *data)
int ret; int ret;
ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER, ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER,
data->buffer, sizeof(data->buffer)); data->scan.chans, sizeof(data->scan.chans));
if (ret < 0) { if (ret < 0) {
dev_err(data->dev, "failed to read axes\n"); dev_err(data->dev, "failed to read axes\n");
return ret; return ret;
@ -301,7 +305,7 @@ static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
if (ret < 0) if (ret < 0)
goto err; goto err;
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
pf->timestamp); pf->timestamp);
err: err:
@ -433,7 +437,7 @@ static int mxc4005_probe(struct i2c_client *client,
data->dready_trig = devm_iio_trigger_alloc(&client->dev, data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d", "%s-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!data->dready_trig) if (!data->dready_trig)
return -ENOMEM; return -ENOMEM;

472
drivers/iio/accel/sca3300.c Normal file
View File

@ -0,0 +1,472 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Murata SCA3300 3-axis industrial accelerometer
*
* Copyright (c) 2021 Vaisala Oyj. All rights reserved.
*/
#include <linux/bitops.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <asm/unaligned.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>
#define SCA3300_ALIAS "sca3300"
#define SCA3300_CRC8_POLYNOMIAL 0x1d
/* Device mode register */
#define SCA3300_REG_MODE 0xd
#define SCA3300_MODE_SW_RESET 0x20
/* Last register in map */
#define SCA3300_REG_SELBANK 0x1f
/* Device status and mask */
#define SCA3300_REG_STATUS 0x6
#define SCA3300_STATUS_MASK GENMASK(8, 0)
/* Device ID */
#define SCA3300_REG_WHOAMI 0x10
#define SCA3300_WHOAMI_ID 0x51
/* Device return status and mask */
#define SCA3300_VALUE_RS_ERROR 0x3
#define SCA3300_MASK_RS_STATUS GENMASK(1, 0)
enum sca3300_scan_indexes {
SCA3300_ACC_X = 0,
SCA3300_ACC_Y,
SCA3300_ACC_Z,
SCA3300_TEMP,
SCA3300_TIMESTAMP,
};
#define SCA3300_ACCEL_CHANNEL(index, reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec sca3300_channels[] = {
SCA3300_ACCEL_CHANNEL(SCA3300_ACC_X, 0x1, X),
SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Y, 0x2, Y),
SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Z, 0x3, Z),
{
.type = IIO_TEMP,
.address = 0x5,
.scan_index = SCA3300_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const int sca3300_lp_freq[] = {70, 70, 70, 10};
static const int sca3300_accel_scale[][2] = {{0, 370}, {0, 741}, {0, 185}, {0, 185}};
static const unsigned long sca3300_scan_masks[] = {
BIT(SCA3300_ACC_X) | BIT(SCA3300_ACC_Y) | BIT(SCA3300_ACC_Z) |
BIT(SCA3300_TEMP),
0
};
/**
* struct sca3300_data - device data
* @spi: SPI device structure
* @lock: Data buffer lock
* @scan: Triggered buffer. Four channel 16-bit data + 64-bit timestamp
* @txbuf: Transmit buffer
* @rxbuf: Receive buffer
*/
struct sca3300_data {
struct spi_device *spi;
struct mutex lock;
struct {
s16 channels[4];
s64 ts __aligned(sizeof(s64));
} scan;
u8 txbuf[4] ____cacheline_aligned;
u8 rxbuf[4];
};
DECLARE_CRC8_TABLE(sca3300_crc_table);
static int sca3300_transfer(struct sca3300_data *sca_data, int *val)
{
/* Consecutive requests min. 10 us delay (Datasheet section 5.1.2) */
struct spi_delay delay = { .value = 10, .unit = SPI_DELAY_UNIT_USECS };
int32_t ret;
int rs;
u8 crc;
struct spi_transfer xfers[2] = {
{
.tx_buf = sca_data->txbuf,
.len = ARRAY_SIZE(sca_data->txbuf),
.delay = delay,
.cs_change = 1,
},
{
.rx_buf = sca_data->rxbuf,
.len = ARRAY_SIZE(sca_data->rxbuf),
.delay = delay,
}
};
/* inverted crc value as described in device data sheet */
crc = ~crc8(sca3300_crc_table, &sca_data->txbuf[0], 3, CRC8_INIT_VALUE);
sca_data->txbuf[3] = crc;
ret = spi_sync_transfer(sca_data->spi, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&sca_data->spi->dev,
"transfer error, error: %d\n", ret);
return -EIO;
}
crc = ~crc8(sca3300_crc_table, &sca_data->rxbuf[0], 3, CRC8_INIT_VALUE);
if (sca_data->rxbuf[3] != crc) {
dev_err(&sca_data->spi->dev, "CRC checksum mismatch");
return -EIO;
}
/* get return status */
rs = sca_data->rxbuf[0] & SCA3300_MASK_RS_STATUS;
if (rs == SCA3300_VALUE_RS_ERROR)
ret = -EINVAL;
*val = sign_extend32(get_unaligned_be16(&sca_data->rxbuf[1]), 15);
return ret;
}
static int sca3300_error_handler(struct sca3300_data *sca_data)
{
int ret;
int val;
mutex_lock(&sca_data->lock);
sca_data->txbuf[0] = SCA3300_REG_STATUS << 2;
ret = sca3300_transfer(sca_data, &val);
mutex_unlock(&sca_data->lock);
/*
* Return status error is cleared after reading status register once,
* expect EINVAL here.
*/
if (ret != -EINVAL) {
dev_err(&sca_data->spi->dev,
"error reading device status: %d\n", ret);
return ret;
}
dev_err(&sca_data->spi->dev, "device status: 0x%lx\n",
val & SCA3300_STATUS_MASK);
return 0;
}
static int sca3300_read_reg(struct sca3300_data *sca_data, u8 reg, int *val)
{
int ret;
mutex_lock(&sca_data->lock);
sca_data->txbuf[0] = reg << 2;
ret = sca3300_transfer(sca_data, val);
mutex_unlock(&sca_data->lock);
if (ret != -EINVAL)
return ret;
return sca3300_error_handler(sca_data);
}
static int sca3300_write_reg(struct sca3300_data *sca_data, u8 reg, int val)
{
int reg_val = 0;
int ret;
mutex_lock(&sca_data->lock);
/* BIT(7) for write operation */
sca_data->txbuf[0] = BIT(7) | (reg << 2);
put_unaligned_be16(val, &sca_data->txbuf[1]);
ret = sca3300_transfer(sca_data, &reg_val);
mutex_unlock(&sca_data->lock);
if (ret != -EINVAL)
return ret;
return sca3300_error_handler(sca_data);
}
static int sca3300_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct sca3300_data *data = iio_priv(indio_dev);
int reg_val;
int ret;
int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
if (val)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(sca3300_accel_scale); i++) {
if (val2 == sca3300_accel_scale[i][1])
return sca3300_write_reg(data, SCA3300_REG_MODE, i);
}
return -EINVAL;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
ret = sca3300_read_reg(data, SCA3300_REG_MODE, &reg_val);
if (ret)
return ret;
/* freq. change is possible only for mode 3 and 4 */
if (reg_val == 2 && val == sca3300_lp_freq[3])
return sca3300_write_reg(data, SCA3300_REG_MODE, 3);
if (reg_val == 3 && val == sca3300_lp_freq[2])
return sca3300_write_reg(data, SCA3300_REG_MODE, 2);
return -EINVAL;
default:
return -EINVAL;
}
}
static int sca3300_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct sca3300_data *data = iio_priv(indio_dev);
int ret;
int reg_val;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = sca3300_read_reg(data, chan->address, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = sca3300_read_reg(data, SCA3300_REG_MODE, &reg_val);
if (ret)
return ret;
*val = 0;
*val2 = sca3300_accel_scale[reg_val][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
ret = sca3300_read_reg(data, SCA3300_REG_MODE, &reg_val);
if (ret)
return ret;
*val = sca3300_lp_freq[reg_val];
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static irqreturn_t sca3300_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct sca3300_data *data = iio_priv(indio_dev);
int bit, ret, val, i = 0;
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = sca3300_read_reg(data, sca3300_channels[bit].address,
&val);
if (ret) {
dev_err_ratelimited(&data->spi->dev,
"failed to read register, error: %d\n", ret);
/* handled, but bailing out due to errors */
goto out;
}
data->scan.channels[i++] = val;
}
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
iio_get_time_ns(indio_dev));
out:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
/*
* sca3300_init - Device init sequence. See datasheet rev 2 section
* 4.2 Start-Up Sequence for details.
*/
static int sca3300_init(struct sca3300_data *sca_data,
struct iio_dev *indio_dev)
{
int value = 0;
int ret;
ret = sca3300_write_reg(sca_data, SCA3300_REG_MODE,
SCA3300_MODE_SW_RESET);
if (ret)
return ret;
/*
* Wait 1ms after SW-reset command.
* Wait 15ms for settling of signal paths.
*/
usleep_range(16e3, 50e3);
ret = sca3300_read_reg(sca_data, SCA3300_REG_WHOAMI, &value);
if (ret)
return ret;
if (value != SCA3300_WHOAMI_ID) {
dev_err(&sca_data->spi->dev,
"device id not expected value, %d != %u\n",
value, SCA3300_WHOAMI_ID);
return -ENODEV;
}
return 0;
}
static int sca3300_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned int reg, unsigned int writeval,
unsigned int *readval)
{
struct sca3300_data *data = iio_priv(indio_dev);
int value;
int ret;
if (reg > SCA3300_REG_SELBANK)
return -EINVAL;
if (!readval)
return sca3300_write_reg(data, reg, writeval);
ret = sca3300_read_reg(data, reg, &value);
if (ret)
return ret;
*readval = value;
return 0;
}
static int sca3300_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)sca3300_accel_scale;
*length = ARRAY_SIZE(sca3300_accel_scale) * 2 - 2;
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = &sca3300_lp_freq[2];
*length = 2;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static const struct iio_info sca3300_info = {
.read_raw = sca3300_read_raw,
.write_raw = sca3300_write_raw,
.debugfs_reg_access = &sca3300_debugfs_reg_access,
.read_avail = sca3300_read_avail,
};
static int sca3300_probe(struct spi_device *spi)
{
struct sca3300_data *sca_data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*sca_data));
if (!indio_dev)
return -ENOMEM;
sca_data = iio_priv(indio_dev);
mutex_init(&sca_data->lock);
sca_data->spi = spi;
crc8_populate_msb(sca3300_crc_table, SCA3300_CRC8_POLYNOMIAL);
indio_dev->info = &sca3300_info;
indio_dev->name = SCA3300_ALIAS;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = sca3300_channels;
indio_dev->num_channels = ARRAY_SIZE(sca3300_channels);
indio_dev->available_scan_masks = sca3300_scan_masks;
ret = sca3300_init(sca_data, indio_dev);
if (ret) {
dev_err(&spi->dev, "failed to init device, error: %d\n", ret);
return ret;
}
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
iio_pollfunc_store_time,
sca3300_trigger_handler, NULL);
if (ret) {
dev_err(&spi->dev,
"iio triggered buffer setup failed, error: %d\n", ret);
return ret;
}
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret) {
dev_err(&spi->dev, "iio device register failed, error: %d\n",
ret);
}
return ret;
}
static const struct of_device_id sca3300_dt_ids[] = {
{ .compatible = "murata,sca3300"},
{}
};
MODULE_DEVICE_TABLE(of, sca3300_dt_ids);
static struct spi_driver sca3300_driver = {
.driver = {
.name = SCA3300_ALIAS,
.of_match_table = sca3300_dt_ids,
},
.probe = sca3300_probe,
};
module_spi_driver(sca3300_driver);
MODULE_AUTHOR("Tomas Melin <tomas.melin@vaisala.com>");
MODULE_DESCRIPTION("Murata SCA3300 SPI Accelerometer");
MODULE_LICENSE("GPL v2");

View File

@ -62,18 +62,6 @@ enum st_accel_type {
#define LIS2DE12_ACCEL_DEV_NAME "lis2de12" #define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
#define LIS2HH12_ACCEL_DEV_NAME "lis2hh12" #define LIS2HH12_ACCEL_DEV_NAME "lis2hh12"
/**
* struct st_sensors_platform_data - default accel platform data
* @drdy_int_pin: default accel DRDY is available on INT1 pin.
*/
static __maybe_unused const struct st_sensors_platform_data default_accel_pdata = {
.drdy_int_pin = 1,
};
const struct st_sensor_settings *st_accel_get_settings(const char *name);
int st_accel_common_probe(struct iio_dev *indio_dev);
void st_accel_common_remove(struct iio_dev *indio_dev);
#ifdef CONFIG_IIO_BUFFER #ifdef CONFIG_IIO_BUFFER
int st_accel_allocate_ring(struct iio_dev *indio_dev); int st_accel_allocate_ring(struct iio_dev *indio_dev);
void st_accel_deallocate_ring(struct iio_dev *indio_dev); void st_accel_deallocate_ring(struct iio_dev *indio_dev);

View File

@ -41,51 +41,74 @@
#define ST_ACCEL_FS_AVL_200G 200 #define ST_ACCEL_FS_AVL_200G 200
#define ST_ACCEL_FS_AVL_400G 400 #define ST_ACCEL_FS_AVL_400G 400
static const struct iio_mount_matrix *
st_accel_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 st_accel_mount_matrix_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_accel_get_mount_matrix),
{ }
};
static const struct iio_chan_spec st_accel_8bit_channels[] = { static const struct iio_chan_spec st_accel_8bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8, ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1), ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1,
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8, ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1), ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1,
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8, ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1), ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1,
st_accel_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3) IIO_CHAN_SOFT_TIMESTAMP(3)
}; };
static const struct iio_chan_spec st_accel_12bit_channels[] = { static const struct iio_chan_spec st_accel_12bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16, ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR), ST_ACCEL_DEFAULT_OUT_X_L_ADDR,
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16, ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), ST_ACCEL_DEFAULT_OUT_Y_L_ADDR,
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16, ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), ST_ACCEL_DEFAULT_OUT_Z_L_ADDR,
st_accel_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3) IIO_CHAN_SOFT_TIMESTAMP(3)
}; };
static const struct iio_chan_spec st_accel_16bit_channels[] = { static const struct iio_chan_spec st_accel_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR), ST_ACCEL_DEFAULT_OUT_X_L_ADDR,
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), ST_ACCEL_DEFAULT_OUT_Y_L_ADDR,
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), ST_ACCEL_DEFAULT_OUT_Z_L_ADDR,
st_accel_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3) IIO_CHAN_SOFT_TIMESTAMP(3)
}; };
@ -980,7 +1003,99 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.multi_read_bit = true, .multi_read_bit = true,
.bootime = 2, .bootime = 2,
}, },
{
.wai = 0x49,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM9DS0_IMU_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
.addr = 0x20,
.mask = GENMASK(7, 4),
.odr_avl = {
{ 3, 0x01, },
{ 6, 0x02, },
{ 12, 0x03, },
{ 25, 0x04, },
{ 50, 0x05, },
{ 100, 0x06, },
{ 200, 0x07, },
{ 400, 0x08, },
{ 800, 0x09, },
{ 1600, 0x0a, },
},
},
.pw = {
.addr = 0x20,
.mask = GENMASK(7, 4),
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0x21,
.mask = GENMASK(5, 3),
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(61),
},
[1] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(122),
},
[2] = {
.num = ST_ACCEL_FS_AVL_6G,
.value = 0x02,
.gain = IIO_G_TO_M_S_2(183),
},
[3] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(244),
},
[4] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0x04,
.gain = IIO_G_TO_M_S_2(732),
},
},
},
.bdu = {
.addr = 0x20,
.mask = BIT(3),
},
.drdy_irq = {
.int1 = {
.addr = 0x22,
.mask = BIT(2),
},
.int2 = {
.addr = 0x23,
.mask = BIT(3),
},
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = GENMASK(2, 0),
},
},
.sim = {
.addr = 0x21,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
};
/* Default accel DRDY is available on INT1 pin */
static const struct st_sensors_platform_data default_accel_pdata = {
.drdy_int_pin = 1,
}; };
static int st_accel_read_raw(struct iio_dev *indio_dev, static int st_accel_read_raw(struct iio_dev *indio_dev,
@ -1070,25 +1185,10 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
#endif #endif
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
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 /* Read ST-specific _ONT orientation data from ACPI and generate an
* appropriate mount matrix. * appropriate mount matrix.
*/ */
static int apply_acpi_orientation(struct iio_dev *indio_dev, static int apply_acpi_orientation(struct iio_dev *indio_dev)
struct iio_chan_spec *channels)
{ {
struct st_sensor_data *adata = iio_priv(indio_dev); struct st_sensor_data *adata = iio_priv(indio_dev);
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
@ -1177,14 +1277,6 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev,
} }
/* Convert our integer matrix to a string-based iio_mount_matrix */ /* 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 (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
int matrix_val = final_ont[i][j]; int matrix_val = final_ont[i][j];
@ -1203,26 +1295,25 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev,
default: default:
goto out; goto out;
} }
adata->mount_matrix->rotation[i * 3 + j] = str_value; 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; ret = 0;
dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n"); dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n");
out: out:
kfree(buffer.pointer); kfree(buffer.pointer);
if (ret)
dev_dbg(&indio_dev->dev,
"failed to apply ACPI orientation data: %d\n", ret);
return ret; return ret;
} }
#else /* !CONFIG_ACPI */ #else /* !CONFIG_ACPI */
static int apply_acpi_orientation(struct iio_dev *indio_dev, static int apply_acpi_orientation(struct iio_dev *indio_dev)
struct iio_chan_spec *channels)
{ {
return 0; return -EINVAL;
} }
#endif #endif
@ -1248,38 +1339,30 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
{ {
struct st_sensor_data *adata = iio_priv(indio_dev); struct st_sensor_data *adata = iio_priv(indio_dev);
struct st_sensors_platform_data *pdata = dev_get_platdata(adata->dev); struct st_sensors_platform_data *pdata = dev_get_platdata(adata->dev);
struct iio_chan_spec *channels;
size_t channels_size;
int err; int err;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &accel_info; indio_dev->info = &accel_info;
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
err = st_sensors_verify_id(indio_dev); err = st_sensors_verify_id(indio_dev);
if (err < 0) if (err < 0)
goto st_accel_power_off; return err;
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
indio_dev->channels = adata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; 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, * First try specific ACPI methods to retrieve orientation then try the
adata->sensor_settings->ch, * generic function.
channels_size, GFP_KERNEL); */
if (!channels) { err = apply_acpi_orientation(indio_dev);
err = -ENOMEM; if (err) {
goto st_accel_power_off; err = iio_read_mount_matrix(adata->dev, &adata->mount_matrix);
if (err)
return err;
} }
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 = &adata->sensor_settings->fs.fs_avl[0]; adata->current_fullscale = &adata->sensor_settings->fs.fs_avl[0];
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz; adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
@ -1288,11 +1371,11 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
err = st_sensors_init_sensor(indio_dev, pdata); err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0) if (err < 0)
goto st_accel_power_off; return err;
err = st_accel_allocate_ring(indio_dev); err = st_accel_allocate_ring(indio_dev);
if (err < 0) if (err < 0)
goto st_accel_power_off; return err;
if (adata->irq > 0) { if (adata->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev, err = st_sensors_allocate_trigger(indio_dev,
@ -1315,9 +1398,6 @@ st_accel_device_register_error:
st_sensors_deallocate_trigger(indio_dev); st_sensors_deallocate_trigger(indio_dev);
st_accel_probe_trigger_error: st_accel_probe_trigger_error:
st_accel_deallocate_ring(indio_dev); st_accel_deallocate_ring(indio_dev);
st_accel_power_off:
st_sensors_power_disable(indio_dev);
return err; return err;
} }
EXPORT_SYMBOL(st_accel_common_probe); EXPORT_SYMBOL(st_accel_common_probe);
@ -1326,8 +1406,6 @@ void st_accel_common_remove(struct iio_dev *indio_dev)
{ {
struct st_sensor_data *adata = iio_priv(indio_dev); struct st_sensor_data *adata = iio_priv(indio_dev);
st_sensors_power_disable(indio_dev);
iio_device_unregister(indio_dev); iio_device_unregister(indio_dev);
if (adata->irq > 0) if (adata->irq > 0)
st_sensors_deallocate_trigger(indio_dev); st_sensors_deallocate_trigger(indio_dev);

View File

@ -174,16 +174,29 @@ static int st_accel_i2c_probe(struct i2c_client *client)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = st_accel_common_probe(indio_dev); ret = st_sensors_power_enable(indio_dev);
if (ret < 0) if (ret)
return ret; return ret;
ret = st_accel_common_probe(indio_dev);
if (ret < 0)
goto st_accel_power_off;
return 0; return 0;
st_accel_power_off:
st_sensors_power_disable(indio_dev);
return ret;
} }
static int st_accel_i2c_remove(struct i2c_client *client) static int st_accel_i2c_remove(struct i2c_client *client)
{ {
st_accel_common_remove(i2c_get_clientdata(client)); struct iio_dev *indio_dev = i2c_get_clientdata(client);
st_sensors_power_disable(indio_dev);
st_accel_common_remove(indio_dev);
return 0; return 0;
} }

View File

@ -123,16 +123,29 @@ static int st_accel_spi_probe(struct spi_device *spi)
if (err < 0) if (err < 0)
return err; return err;
err = st_accel_common_probe(indio_dev); err = st_sensors_power_enable(indio_dev);
if (err < 0) if (err)
return err; return err;
err = st_accel_common_probe(indio_dev);
if (err < 0)
goto st_accel_power_off;
return 0; return 0;
st_accel_power_off:
st_sensors_power_disable(indio_dev);
return err;
} }
static int st_accel_spi_remove(struct spi_device *spi) static int st_accel_spi_remove(struct spi_device *spi)
{ {
st_accel_common_remove(spi_get_drvdata(spi)); struct iio_dev *indio_dev = spi_get_drvdata(spi);
st_sensors_power_disable(indio_dev);
st_accel_common_remove(indio_dev);
return 0; return 0;
} }

View File

@ -7,7 +7,6 @@
* IIO driver for STK8312; 7-bit I2C address: 0x3D. * IIO driver for STK8312; 7-bit I2C address: 0x3D.
*/ */
#include <linux/acpi.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
@ -103,7 +102,11 @@ struct stk8312_data {
u8 mode; u8 mode;
struct iio_trigger *dready_trig; struct iio_trigger *dready_trig;
bool dready_trigger_on; bool dready_trigger_on;
s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 64-bit timestamp */ /* Ensure timestamp is naturally aligned */
struct {
s8 chans[3];
s64 timestamp __aligned(8);
} scan;
}; };
static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL); static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
@ -438,7 +441,7 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p)
ret = i2c_smbus_read_i2c_block_data(data->client, ret = i2c_smbus_read_i2c_block_data(data->client,
STK8312_REG_XOUT, STK8312_REG_XOUT,
STK8312_ALL_CHANNEL_SIZE, STK8312_ALL_CHANNEL_SIZE,
data->buffer); data->scan.chans);
if (ret < STK8312_ALL_CHANNEL_SIZE) { if (ret < STK8312_ALL_CHANNEL_SIZE) {
dev_err(&data->client->dev, "register read failed\n"); dev_err(&data->client->dev, "register read failed\n");
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
@ -452,12 +455,12 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p)
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
goto err; goto err;
} }
data->buffer[i++] = ret; data->scan.chans[i++] = ret;
} }
} }
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
pf->timestamp); pf->timestamp);
err: err:
iio_trigger_notify_done(indio_dev->trig); iio_trigger_notify_done(indio_dev->trig);
@ -552,7 +555,7 @@ static int stk8312_probe(struct i2c_client *client,
data->dready_trig = devm_iio_trigger_alloc(&client->dev, data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d", "%s-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!data->dready_trig) { if (!data->dready_trig) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_power_off; goto err_power_off;
@ -635,23 +638,17 @@ static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
#endif #endif
static const struct i2c_device_id stk8312_i2c_id[] = { static const struct i2c_device_id stk8312_i2c_id[] = {
{"STK8312", 0}, /* Deprecated in favour of lowercase form */
{ "STK8312", 0 },
{ "stk8312", 0 },
{} {}
}; };
MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id); MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id);
static const struct acpi_device_id stk8312_acpi_id[] = {
{"STK8312", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
static struct i2c_driver stk8312_driver = { static struct i2c_driver stk8312_driver = {
.driver = { .driver = {
.name = STK8312_DRIVER_NAME, .name = STK8312_DRIVER_NAME,
.pm = STK8312_PM_OPS, .pm = STK8312_PM_OPS,
.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
}, },
.probe = stk8312_probe, .probe = stk8312_probe,
.remove = stk8312_remove, .remove = stk8312_remove,

View File

@ -91,12 +91,11 @@ struct stk8ba50_data {
u8 sample_rate_idx; u8 sample_rate_idx;
struct iio_trigger *dready_trig; struct iio_trigger *dready_trig;
bool dready_trigger_on; bool dready_trigger_on;
/* /* Ensure timestamp is naturally aligned */
* 3 x 16-bit channels (10-bit data, 6-bit padding) + struct {
* 1 x 16 padding + s16 chans[3];
* 4 x 16 64-bit timestamp s64 timetamp __aligned(8);
*/ } scan;
s16 buffer[8];
}; };
#define STK8BA50_ACCEL_CHANNEL(index, reg, axis) { \ #define STK8BA50_ACCEL_CHANNEL(index, reg, axis) { \
@ -324,7 +323,7 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p)
ret = i2c_smbus_read_i2c_block_data(data->client, ret = i2c_smbus_read_i2c_block_data(data->client,
STK8BA50_REG_XOUT, STK8BA50_REG_XOUT,
STK8BA50_ALL_CHANNEL_SIZE, STK8BA50_ALL_CHANNEL_SIZE,
(u8 *)data->buffer); (u8 *)data->scan.chans);
if (ret < STK8BA50_ALL_CHANNEL_SIZE) { if (ret < STK8BA50_ALL_CHANNEL_SIZE) {
dev_err(&data->client->dev, "register read failed\n"); dev_err(&data->client->dev, "register read failed\n");
goto err; goto err;
@ -337,10 +336,10 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p)
if (ret < 0) if (ret < 0)
goto err; goto err;
data->buffer[i++] = ret; data->scan.chans[i++] = ret;
} }
} }
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
pf->timestamp); pf->timestamp);
err: err:
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
@ -448,7 +447,7 @@ static int stk8ba50_probe(struct i2c_client *client,
data->dready_trig = devm_iio_trigger_alloc(&client->dev, data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d", "%s-dev%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (!data->dready_trig) { if (!data->dready_trig) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_power_off; goto err_power_off;

View File

@ -1190,6 +1190,18 @@ config TI_TLC4541
This driver can also be built as a module. If so, the module will be This driver can also be built as a module. If so, the module will be
called ti-tlc4541. called ti-tlc4541.
config TI_TSC2046
tristate "Texas Instruments TSC2046 ADC driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for ADC functionality of Texas
Instruments TSC2046 touch screen controller.
This driver can also be built as a module. If so, the module will be
called ti-tsc2046.
config TWL4030_MADC config TWL4030_MADC
tristate "TWL4030 MADC (Monitoring A/D Converter)" tristate "TWL4030 MADC (Monitoring A/D Converter)"
depends on TWL4030_CORE depends on TWL4030_CORE

View File

@ -106,6 +106,7 @@ obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o

View File

@ -13,6 +13,7 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/bitops.h> #include <linux/bitops.h>
@ -346,6 +347,12 @@ static int ad7298_probe(struct spi_device *spi)
return devm_iio_device_register(&spi->dev, indio_dev); return devm_iio_device_register(&spi->dev, indio_dev);
} }
static const struct acpi_device_id ad7298_acpi_ids[] = {
{ "INT3494", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids);
static const struct spi_device_id ad7298_id[] = { static const struct spi_device_id ad7298_id[] = {
{"ad7298", 0}, {"ad7298", 0},
{} {}
@ -355,6 +362,7 @@ MODULE_DEVICE_TABLE(spi, ad7298_id);
static struct spi_driver ad7298_driver = { static struct spi_driver ad7298_driver = {
.driver = { .driver = {
.name = "ad7298", .name = "ad7298",
.acpi_match_table = ad7298_acpi_ids,
}, },
.probe = ad7298_probe, .probe = ad7298_probe,
.id_table = ad7298_id, .id_table = ad7298_id,

View File

@ -32,12 +32,14 @@ struct ad7476_chip_info {
/* channels used when convst gpio is defined */ /* channels used when convst gpio is defined */
struct iio_chan_spec convst_channel[2]; struct iio_chan_spec convst_channel[2];
void (*reset)(struct ad7476_state *); void (*reset)(struct ad7476_state *);
bool has_vref;
bool has_vdrive;
}; };
struct ad7476_state { struct ad7476_state {
struct spi_device *spi; struct spi_device *spi;
const struct ad7476_chip_info *chip_info; const struct ad7476_chip_info *chip_info;
struct regulator *reg; struct regulator *ref_reg;
struct gpio_desc *convst_gpio; struct gpio_desc *convst_gpio;
struct spi_transfer xfer; struct spi_transfer xfer;
struct spi_message msg; struct spi_message msg;
@ -52,13 +54,17 @@ struct ad7476_state {
}; };
enum ad7476_supported_device_ids { enum ad7476_supported_device_ids {
ID_AD7091,
ID_AD7091R, ID_AD7091R,
ID_AD7273,
ID_AD7274,
ID_AD7276, ID_AD7276,
ID_AD7277, ID_AD7277,
ID_AD7278, ID_AD7278,
ID_AD7466, ID_AD7466,
ID_AD7467, ID_AD7467,
ID_AD7468, ID_AD7468,
ID_AD7475,
ID_AD7495, ID_AD7495,
ID_AD7940, ID_AD7940,
ID_ADC081S, ID_ADC081S,
@ -145,8 +151,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0); GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0);
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_SCALE:
if (!st->chip_info->int_vref_uv) { if (st->ref_reg) {
scale_uv = regulator_get_voltage(st->reg); scale_uv = regulator_get_voltage(st->ref_reg);
if (scale_uv < 0) if (scale_uv < 0)
return scale_uv; return scale_uv;
} else { } else {
@ -187,13 +193,32 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
BIT(IIO_CHAN_INFO_RAW)) BIT(IIO_CHAN_INFO_RAW))
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
[ID_AD7091R] = { [ID_AD7091] = {
.channel[0] = AD7091R_CHAN(12), .channel[0] = AD7091R_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.convst_channel[0] = AD7091R_CONVST_CHAN(12), .convst_channel[0] = AD7091R_CONVST_CHAN(12),
.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.reset = ad7091_reset, .reset = ad7091_reset,
}, },
[ID_AD7091R] = {
.channel[0] = AD7091R_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.convst_channel[0] = AD7091R_CONVST_CHAN(12),
.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.int_vref_uv = 2500000,
.has_vref = true,
.reset = ad7091_reset,
},
[ID_AD7273] = {
.channel[0] = AD7940_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
},
[ID_AD7274] = {
.channel[0] = AD7940_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
},
[ID_AD7276] = { [ID_AD7276] = {
.channel[0] = AD7940_CHAN(12), .channel[0] = AD7940_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
@ -218,10 +243,17 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
.channel[0] = AD7476_CHAN(8), .channel[0] = AD7476_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
}, },
[ID_AD7475] = {
.channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
.has_vdrive = true,
},
[ID_AD7495] = { [ID_AD7495] = {
.channel[0] = AD7476_CHAN(12), .channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.int_vref_uv = 2500000, .int_vref_uv = 2500000,
.has_vdrive = true,
}, },
[ID_AD7940] = { [ID_AD7940] = {
.channel[0] = AD7940_CHAN(14), .channel[0] = AD7940_CHAN(14),
@ -254,6 +286,7 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
[ID_LTC2314_14] = { [ID_LTC2314_14] = {
.channel[0] = AD7940_CHAN(14), .channel[0] = AD7940_CHAN(14),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
}, },
}; };
@ -263,15 +296,16 @@ static const struct iio_info ad7476_info = {
static void ad7476_reg_disable(void *data) static void ad7476_reg_disable(void *data)
{ {
struct ad7476_state *st = data; struct regulator *reg = data;
regulator_disable(st->reg); regulator_disable(reg);
} }
static int ad7476_probe(struct spi_device *spi) static int ad7476_probe(struct spi_device *spi)
{ {
struct ad7476_state *st; struct ad7476_state *st;
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
struct regulator *reg;
int ret; int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
@ -282,19 +316,73 @@ static int ad7476_probe(struct spi_device *spi)
st->chip_info = st->chip_info =
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data]; &ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
st->reg = devm_regulator_get(&spi->dev, "vcc"); reg = devm_regulator_get(&spi->dev, "vcc");
if (IS_ERR(st->reg)) if (IS_ERR(reg))
return PTR_ERR(st->reg); return PTR_ERR(reg);
ret = regulator_enable(st->reg); ret = regulator_enable(reg);
if (ret) if (ret)
return ret; return ret;
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg);
st);
if (ret) if (ret)
return ret; return ret;
/* Either vcc or vref (below) as appropriate */
if (!st->chip_info->int_vref_uv)
st->ref_reg = reg;
if (st->chip_info->has_vref) {
/* If a device has an internal reference vref is optional */
if (st->chip_info->int_vref_uv) {
reg = devm_regulator_get_optional(&spi->dev, "vref");
if (IS_ERR(reg) && (PTR_ERR(reg) != -ENODEV))
return PTR_ERR(reg);
} else {
reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(reg))
return PTR_ERR(reg);
}
if (!IS_ERR(reg)) {
ret = regulator_enable(reg);
if (ret)
return ret;
ret = devm_add_action_or_reset(&spi->dev,
ad7476_reg_disable,
reg);
if (ret)
return ret;
st->ref_reg = reg;
} else {
/*
* Can only get here if device supports both internal
* and external reference, but the regulator connected
* to the external reference is not connected.
* Set the reference regulator pointer to NULL to
* indicate this.
*/
st->ref_reg = NULL;
}
}
if (st->chip_info->has_vdrive) {
reg = devm_regulator_get(&spi->dev, "vdrive");
if (IS_ERR(reg))
return PTR_ERR(reg);
ret = regulator_enable(reg);
if (ret)
return ret;
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable,
reg);
if (ret)
return ret;
}
st->convst_gpio = devm_gpiod_get_optional(&spi->dev, st->convst_gpio = devm_gpiod_get_optional(&spi->dev,
"adi,conversion-start", "adi,conversion-start",
GPIOD_OUT_LOW); GPIOD_OUT_LOW);
@ -333,17 +421,17 @@ static int ad7476_probe(struct spi_device *spi)
} }
static const struct spi_device_id ad7476_id[] = { static const struct spi_device_id ad7476_id[] = {
{"ad7091", ID_AD7091R}, {"ad7091", ID_AD7091},
{"ad7091r", ID_AD7091R}, {"ad7091r", ID_AD7091R},
{"ad7273", ID_AD7277}, {"ad7273", ID_AD7273},
{"ad7274", ID_AD7276}, {"ad7274", ID_AD7274},
{"ad7276", ID_AD7276}, {"ad7276", ID_AD7276},
{"ad7277", ID_AD7277}, {"ad7277", ID_AD7277},
{"ad7278", ID_AD7278}, {"ad7278", ID_AD7278},
{"ad7466", ID_AD7466}, {"ad7466", ID_AD7466},
{"ad7467", ID_AD7467}, {"ad7467", ID_AD7467},
{"ad7468", ID_AD7468}, {"ad7468", ID_AD7468},
{"ad7475", ID_AD7466}, {"ad7475", ID_AD7475},
{"ad7476", ID_AD7466}, {"ad7476", ID_AD7466},
{"ad7476a", ID_AD7466}, {"ad7476a", ID_AD7466},
{"ad7477", ID_AD7467}, {"ad7477", ID_AD7467},

View File

@ -663,7 +663,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
} }
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name, indio_dev->id); indio_dev->name,
iio_device_id(indio_dev));
if (!st->trig) if (!st->trig)
return -ENOMEM; return -ENOMEM;

View File

@ -248,7 +248,8 @@ static int ad7766_probe(struct spi_device *spi)
if (spi->irq > 0) { if (spi->irq > 0) {
ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
indio_dev->name, indio_dev->id); indio_dev->name,
iio_device_id(indio_dev));
if (!ad7766->trig) if (!ad7766->trig)
return -ENOMEM; return -ENOMEM;
@ -289,10 +290,7 @@ static int ad7766_probe(struct spi_device *spi)
if (ret) if (ret)
return ret; return ret;
ret = devm_iio_device_register(&spi->dev, indio_dev); return devm_iio_device_register(&spi->dev, indio_dev);
if (ret)
return ret;
return 0;
} }
static const struct spi_device_id ad7766_id[] = { static const struct spi_device_id ad7766_id[] = {

View File

@ -630,7 +630,8 @@ static int ad7768_probe(struct spi_device *spi)
} }
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
indio_dev->name, indio_dev->id); indio_dev->name,
iio_device_id(indio_dev));
if (!st->trig) if (!st->trig)
return -ENOMEM; return -ENOMEM;

View File

@ -477,7 +477,7 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
sigma_delta->trig = iio_trigger_alloc(&sigma_delta->spi->dev, sigma_delta->trig = iio_trigger_alloc(&sigma_delta->spi->dev,
"%s-dev%d", indio_dev->name, "%s-dev%d", indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (sigma_delta->trig == NULL) { if (sigma_delta->trig == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto error_ret; goto error_ret;

View File

@ -202,29 +202,25 @@ static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv)
kfree(cl); kfree(cl);
} }
static void devm_adi_axi_adc_conv_release(struct device *dev, void *res) static void devm_adi_axi_adc_conv_release(void *conv)
{ {
adi_axi_adc_conv_unregister(*(struct adi_axi_adc_conv **)res); adi_axi_adc_conv_unregister(conv);
} }
struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev,
size_t sizeof_priv) size_t sizeof_priv)
{ {
struct adi_axi_adc_conv **ptr, *conv; struct adi_axi_adc_conv *conv;
int ret;
ptr = devres_alloc(devm_adi_axi_adc_conv_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
conv = adi_axi_adc_conv_register(dev, sizeof_priv); conv = adi_axi_adc_conv_register(dev, sizeof_priv);
if (IS_ERR(conv)) { if (IS_ERR(conv))
devres_free(ptr); return conv;
return ERR_CAST(conv);
}
*ptr = conv; ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release,
devres_add(dev, ptr); conv);
if (ret)
return ERR_PTR(ret);
return conv; return conv;
} }

View File

@ -997,7 +997,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
int ret; int ret;
trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name, trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name,
indio->id, trigger_name); iio_device_id(indio), trigger_name);
if (!trig) if (!trig)
return NULL; return NULL;

View File

@ -547,7 +547,7 @@ static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
char *name = kasprintf(GFP_KERNEL, char *name = kasprintf(GFP_KERNEL,
"%s-dev%d-%s", "%s-dev%d-%s",
idev->name, idev->name,
idev->id, iio_device_id(idev),
triggers[i].name); triggers[i].name);
if (!name) if (!name)
return -ENOMEM; return -ENOMEM;
@ -626,7 +626,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
int ret; int ret;
trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name, trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name,
idev->id, trigger->name); iio_device_id(idev), trigger->name);
if (trig == NULL) if (trig == NULL)
return NULL; return NULL;

View File

@ -649,7 +649,8 @@ static int dln2_adc_probe(struct platform_device *pdev)
indio_dev->setup_ops = &dln2_adc_buffer_setup_ops; indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name, indio_dev->id); indio_dev->name,
iio_device_id(indio_dev));
if (!dln2->trig) { if (!dln2->trig) {
dev_err(dev, "failed to allocate trigger\n"); dev_err(dev, "failed to allocate trigger\n");
return -ENOMEM; return -ENOMEM;

View File

@ -165,10 +165,8 @@ static int ep93xx_adc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res); priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base)) { if (IS_ERR(priv->base))
dev_err(&pdev->dev, "Cannot map memory resource\n");
return PTR_ERR(priv->base); return PTR_ERR(priv->base);
}
iiodev->name = dev_name(&pdev->dev); iiodev->name = dev_name(&pdev->dev);
iiodev->modes = INDIO_DIRECT_MODE; iiodev->modes = INDIO_DIRECT_MODE;

View File

@ -794,7 +794,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev); struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev);
struct iio_dev *indio_dev = NULL; struct iio_dev *indio_dev = NULL;
bool has_ts = false; bool has_ts = false;
int ret = -ENODEV; int ret;
int irq; int irq;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc)); indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));

View File

@ -843,7 +843,8 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
chip->allow_async_readout); chip->allow_async_readout);
task = kthread_create(ina2xx_capture_thread, (void *)indio_dev, task = kthread_create(ina2xx_capture_thread, (void *)indio_dev,
"%s:%d-%uus", indio_dev->name, indio_dev->id, "%s:%d-%uus", indio_dev->name,
iio_device_id(indio_dev),
sampling_us); sampling_us);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_ERR(task); return PTR_ERR(task);

View File

@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <asm/unaligned.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/driver.h> #include <linux/iio/driver.h>
@ -63,7 +64,7 @@ static int max11100_read_single(struct iio_dev *indio_dev, int *val)
return -EINVAL; return -EINVAL;
} }
*val = (state->buffer[1] << 8) | state->buffer[2]; *val = get_unaligned_be16(&state->buffer[1]);
return 0; return 0;
} }
@ -101,6 +102,11 @@ static const struct iio_info max11100_info = {
.read_raw = max11100_read_raw, .read_raw = max11100_read_raw,
}; };
static void max11100_regulator_disable(void *reg)
{
regulator_disable(reg);
}
static int max11100_probe(struct spi_device *spi) static int max11100_probe(struct spi_device *spi)
{ {
int ret; int ret;
@ -111,8 +117,6 @@ static int max11100_probe(struct spi_device *spi)
if (!indio_dev) if (!indio_dev)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, indio_dev);
state = iio_priv(indio_dev); state = iio_priv(indio_dev);
state->spi = spi; state->spi = spi;
@ -130,27 +134,12 @@ static int max11100_probe(struct spi_device *spi)
if (ret) if (ret)
return ret; return ret;
ret = iio_device_register(indio_dev); ret = devm_add_action_or_reset(&spi->dev, max11100_regulator_disable,
state->vref_reg);
if (ret) if (ret)
goto disable_regulator; return ret;
return 0; return devm_iio_device_register(&spi->dev, indio_dev);
disable_regulator:
regulator_disable(state->vref_reg);
return ret;
}
static int max11100_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max11100_state *state = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
regulator_disable(state->vref_reg);
return 0;
} }
static const struct of_device_id max11100_ids[] = { static const struct of_device_id max11100_ids[] = {
@ -165,7 +154,6 @@ static struct spi_driver max11100_driver = {
.of_match_table = max11100_ids, .of_match_table = max11100_ids,
}, },
.probe = max11100_probe, .probe = max11100_probe,
.remove = max11100_remove,
}; };
module_spi_driver(max11100_driver); module_spi_driver(max11100_driver);

View File

@ -66,9 +66,8 @@ static const struct iio_chan_spec max1118_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(2), IIO_CHAN_SOFT_TIMESTAMP(2),
}; };
static int max1118_read(struct spi_device *spi, int channel) static int max1118_read(struct iio_dev *indio_dev, int channel)
{ {
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev); struct max1118 *adc = iio_priv(indio_dev);
struct spi_transfer xfers[] = { struct spi_transfer xfers[] = {
/* /*
@ -103,9 +102,9 @@ static int max1118_read(struct spi_device *spi, int channel)
int ret; int ret;
if (channel == 0) if (channel == 0)
ret = spi_sync_transfer(spi, xfers + 1, 2); ret = spi_sync_transfer(adc->spi, xfers + 1, 2);
else else
ret = spi_sync_transfer(spi, xfers, 3); ret = spi_sync_transfer(adc->spi, xfers, 3);
if (ret) if (ret)
return ret; return ret;
@ -113,11 +112,10 @@ static int max1118_read(struct spi_device *spi, int channel)
return adc->data; return adc->data;
} }
static int max1118_get_vref_mV(struct spi_device *spi) static int max1118_get_vref_mV(struct iio_dev *indio_dev)
{ {
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev); struct max1118 *adc = iio_priv(indio_dev);
const struct spi_device_id *id = spi_get_device_id(spi); const struct spi_device_id *id = spi_get_device_id(adc->spi);
int vref_uV; int vref_uV;
switch (id->driver_data) { switch (id->driver_data) {
@ -144,14 +142,14 @@ static int max1118_read_raw(struct iio_dev *indio_dev,
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
mutex_lock(&adc->lock); mutex_lock(&adc->lock);
*val = max1118_read(adc->spi, chan->channel); *val = max1118_read(indio_dev, chan->channel);
mutex_unlock(&adc->lock); mutex_unlock(&adc->lock);
if (*val < 0) if (*val < 0)
return *val; return *val;
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_SCALE:
*val = max1118_get_vref_mV(adc->spi); *val = max1118_get_vref_mV(indio_dev);
if (*val < 0) if (*val < 0)
return *val; return *val;
*val2 = 8; *val2 = 8;
@ -180,7 +178,7 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p)
indio_dev->masklength) { indio_dev->masklength) {
const struct iio_chan_spec *scan_chan = const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index]; &indio_dev->channels[scan_index];
int ret = max1118_read(adc->spi, scan_chan->channel); int ret = max1118_read(indio_dev, scan_chan->channel);
if (ret < 0) { if (ret < 0) {
dev_warn(&adc->spi->dev, dev_warn(&adc->spi->dev,
@ -201,6 +199,11 @@ out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void max1118_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int max1118_probe(struct spi_device *spi) static int max1118_probe(struct spi_device *spi)
{ {
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
@ -225,9 +228,13 @@ static int max1118_probe(struct spi_device *spi)
ret = regulator_enable(adc->reg); ret = regulator_enable(adc->reg);
if (ret) if (ret)
return ret; return ret;
}
spi_set_drvdata(spi, indio_dev); ret = devm_add_action_or_reset(&spi->dev, max1118_reg_disable,
adc->reg);
if (ret)
return ret;
}
indio_dev->name = spi_get_device_id(spi)->name; indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->info = &max1118_info; indio_dev->info = &max1118_info;
@ -241,40 +248,14 @@ static int max1118_probe(struct spi_device *spi)
* a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
* into AutoShutdown mode until the next conversion is initiated. * into AutoShutdown mode until the next conversion is initiated.
*/ */
max1118_read(spi, 0); max1118_read(indio_dev, 0);
ret = iio_triggered_buffer_setup(indio_dev, NULL, ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
max1118_trigger_handler, NULL); max1118_trigger_handler, NULL);
if (ret) if (ret)
goto err_reg_disable; return ret;
ret = iio_device_register(indio_dev); return devm_iio_device_register(&spi->dev, indio_dev);
if (ret)
goto err_buffer_cleanup;
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_reg_disable:
if (id->driver_data == max1118)
regulator_disable(adc->reg);
return ret;
}
static int max1118_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
const struct spi_device_id *id = spi_get_device_id(spi);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (id->driver_data == max1118)
return regulator_disable(adc->reg);
return 0;
} }
static const struct spi_device_id max1118_id[] = { static const struct spi_device_id max1118_id[] = {
@ -299,7 +280,6 @@ static struct spi_driver max1118_spi_driver = {
.of_match_table = max1118_dt_ids, .of_match_table = max1118_dt_ids,
}, },
.probe = max1118_probe, .probe = max1118_probe,
.remove = max1118_remove,
.id_table = max1118_id, .id_table = max1118_id,
}; };
module_spi_driver(max1118_spi_driver); module_spi_driver(max1118_spi_driver);

View File

@ -144,7 +144,6 @@ static int mp2629_adc_probe(struct platform_device *pdev)
} }
indio_dev->name = "mp2629-adc"; indio_dev->name = "mp2629-adc";
indio_dev->dev.parent = dev;
indio_dev->channels = mp2629_channels; indio_dev->channels = mp2629_channels;
indio_dev->num_channels = ARRAY_SIZE(mp2629_channels); indio_dev->num_channels = ARRAY_SIZE(mp2629_channels);
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;

View File

@ -337,7 +337,6 @@ static int mt6360_adc_probe(struct platform_device *pdev)
} }
indio_dev->name = dev_name(&pdev->dev); indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &mt6360_adc_iio_info; indio_dev->info = &mt6360_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mt6360_adc_channels; indio_dev->channels = mt6360_adc_channels;

View File

@ -455,7 +455,7 @@ static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
struct mxs_lradc_adc *adc = iio_priv(iio); struct mxs_lradc_adc *adc = iio_priv(iio);
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name, trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
iio->id); iio_device_id(iio));
if (!trig) if (!trig)
return -ENOMEM; return -ENOMEM;

View File

@ -162,18 +162,13 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
{ {
struct device *dev = priv->dev; struct device *dev = priv->dev;
int ret;
if (on) { if (on) {
ret = pm_runtime_get_sync(dev); return pm_runtime_resume_and_get(dev);
if (ret < 0)
pm_runtime_put_noidle(dev);
} else { } else {
pm_runtime_mark_last_busy(dev); pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev); return pm_runtime_put_autosuspend(dev);
} }
return ret;
} }
static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
@ -535,7 +530,10 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(dev); pm_runtime_use_autosuspend(dev);
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret)
goto err_power_up;
rcar_gyroadc_hw_init(priv); rcar_gyroadc_hw_init(priv);
rcar_gyroadc_hw_start(priv); rcar_gyroadc_hw_start(priv);
@ -552,6 +550,7 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
err_iio_device_register: err_iio_device_register:
rcar_gyroadc_hw_stop(priv); rcar_gyroadc_hw_stop(priv);
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
err_power_up:
pm_runtime_disable(dev); pm_runtime_disable(dev);
pm_runtime_set_suspended(dev); pm_runtime_set_suspended(dev);
clk_disable_unprepare(priv->clk); clk_disable_unprepare(priv->clk);

View File

@ -549,6 +549,7 @@ static const struct of_device_id sc27xx_adc_of_match[] = {
{ .compatible = "sprd,sc2731-adc", }, { .compatible = "sprd,sc2731-adc", },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, sc27xx_adc_of_match);
static struct platform_driver sc27xx_adc_driver = { static struct platform_driver sc27xx_adc_driver = {
.probe = sc27xx_adc_probe, .probe = sc27xx_adc_probe,

View File

@ -449,7 +449,7 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
.smp_bits = stm32h7_smp_bits, .smp_bits = stm32h7_smp_bits,
}; };
/** /*
* STM32 ADC registers access routines * STM32 ADC registers access routines
* @adc: stm32 adc instance * @adc: stm32 adc instance
* @reg: reg offset in adc instance * @reg: reg offset in adc instance
@ -851,7 +851,7 @@ static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev)
return 0; return 0;
} }
/** /*
* Fixed timeout value for ADC calibration. * Fixed timeout value for ADC calibration.
* worst cases: * worst cases:
* - low clock frequency * - low clock frequency
@ -1158,11 +1158,9 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
adc->bufi = 0; adc->bufi = 0;
ret = pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(dev);
return ret; return ret;
}
/* Apply sampling time settings */ /* Apply sampling time settings */
stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]); stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]);
@ -1364,11 +1362,9 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
struct device *dev = indio_dev->dev.parent; struct device *dev = indio_dev->dev.parent;
int ret; int ret;
ret = pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(dev);
return ret; return ret;
}
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength); adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
@ -1413,11 +1409,9 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
struct device *dev = indio_dev->dev.parent; struct device *dev = indio_dev->dev.parent;
int ret; int ret;
ret = pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(dev);
return ret; return ret;
}
if (!readval) if (!readval)
stm32_adc_writel(adc, reg, writeval); stm32_adc_writel(adc, reg, writeval);
@ -1537,11 +1531,9 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
struct device *dev = indio_dev->dev.parent; struct device *dev = indio_dev->dev.parent;
int ret; int ret;
ret = pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(dev);
return ret; return ret;
}
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig); ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
if (ret) { if (ret) {

View File

@ -135,11 +135,9 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
int ret; int ret;
if (atomic_inc_return(&priv->n_active_ch) == 1) { if (atomic_inc_return(&priv->n_active_ch) == 1) {
ret = pm_runtime_get_sync(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(dev);
goto error_ret; goto error_ret;
}
/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */ /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
clk_src = priv->aclk ? 1 : 0; clk_src = priv->aclk ? 1 : 0;

View File

@ -146,6 +146,11 @@ out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void adc081c_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int adc081c_probe(struct i2c_client *client, static int adc081c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
@ -175,6 +180,11 @@ static int adc081c_probe(struct i2c_client *client,
if (err < 0) if (err < 0)
return err; return err;
err = devm_add_action_or_reset(&client->dev, adc081c_reg_disable,
adc->ref);
if (err)
return err;
iio->name = dev_name(&client->dev); iio->name = dev_name(&client->dev);
iio->modes = INDIO_DIRECT_MODE; iio->modes = INDIO_DIRECT_MODE;
iio->info = &adc081c_info; iio->info = &adc081c_info;
@ -182,38 +192,14 @@ static int adc081c_probe(struct i2c_client *client,
iio->channels = model->channels; iio->channels = model->channels;
iio->num_channels = ADC081C_NUM_CHANNELS; iio->num_channels = ADC081C_NUM_CHANNELS;
err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL); err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL,
adc081c_trigger_handler, NULL);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n"); dev_err(&client->dev, "iio triggered buffer setup failed\n");
goto err_regulator_disable; return err;
} }
err = iio_device_register(iio); return devm_iio_device_register(&client->dev, iio);
if (err < 0)
goto err_buffer_cleanup;
i2c_set_clientdata(client, iio);
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(iio);
err_regulator_disable:
regulator_disable(adc->ref);
return err;
}
static int adc081c_remove(struct i2c_client *client)
{
struct iio_dev *iio = i2c_get_clientdata(client);
struct adc081c *adc = iio_priv(iio);
iio_device_unregister(iio);
iio_triggered_buffer_cleanup(iio);
regulator_disable(adc->ref);
return 0;
} }
static const struct i2c_device_id adc081c_id[] = { static const struct i2c_device_id adc081c_id[] = {
@ -238,7 +224,6 @@ static struct i2c_driver adc081c_driver = {
.of_match_table = adc081c_of_match, .of_match_table = adc081c_of_match,
}, },
.probe = adc081c_probe, .probe = adc081c_probe,
.remove = adc081c_remove,
.id_table = adc081c_id, .id_table = adc081c_id,
}; };
module_i2c_driver(adc081c_driver); module_i2c_driver(adc081c_driver);

View File

@ -236,6 +236,11 @@ out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void adc0832_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int adc0832_probe(struct spi_device *spi) static int adc0832_probe(struct spi_device *spi)
{ {
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
@ -287,36 +292,17 @@ static int adc0832_probe(struct spi_device *spi)
if (ret) if (ret)
return ret; return ret;
spi_set_drvdata(spi, indio_dev); ret = devm_add_action_or_reset(&spi->dev, adc0832_reg_disable,
adc->reg);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
adc0832_trigger_handler, NULL);
if (ret) if (ret)
goto err_reg_disable; return ret;
ret = iio_device_register(indio_dev); ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
adc0832_trigger_handler, NULL);
if (ret) if (ret)
goto err_buffer_cleanup; return ret;
return 0; return devm_iio_device_register(&spi->dev, indio_dev);
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_reg_disable:
regulator_disable(adc->reg);
return ret;
}
static int adc0832_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct adc0832 *adc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(adc->reg);
return 0;
} }
static const struct of_device_id adc0832_dt_ids[] = { static const struct of_device_id adc0832_dt_ids[] = {
@ -343,7 +329,6 @@ static struct spi_driver adc0832_driver = {
.of_match_table = adc0832_dt_ids, .of_match_table = adc0832_dt_ids,
}, },
.probe = adc0832_probe, .probe = adc0832_probe,
.remove = adc0832_remove,
.id_table = adc0832_id, .id_table = adc0832_id,
}; };
module_spi_driver(adc0832_driver); module_spi_driver(adc0832_driver);

View File

@ -215,6 +215,11 @@ static const struct iio_info adc108s102_info = {
.update_scan_mode = &adc108s102_update_scan_mode, .update_scan_mode = &adc108s102_update_scan_mode,
}; };
static void adc108s102_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int adc108s102_probe(struct spi_device *spi) static int adc108s102_probe(struct spi_device *spi)
{ {
struct adc108s102_state *st; struct adc108s102_state *st;
@ -239,6 +244,10 @@ static int adc108s102_probe(struct spi_device *spi)
dev_err(&spi->dev, "Cannot enable vref regulator\n"); dev_err(&spi->dev, "Cannot enable vref regulator\n");
return ret; return ret;
} }
ret = devm_add_action_or_reset(&spi->dev, adc108s102_reg_disable,
st->reg);
if (ret)
return ret;
ret = regulator_get_voltage(st->reg); ret = regulator_get_voltage(st->reg);
if (ret < 0) { if (ret < 0) {
@ -249,7 +258,6 @@ static int adc108s102_probe(struct spi_device *spi)
st->va_millivolt = ret / 1000; st->va_millivolt = ret / 1000;
} }
spi_set_drvdata(spi, indio_dev);
st->spi = spi; st->spi = spi;
indio_dev->name = spi->modalias; indio_dev->name = spi->modalias;
@ -266,40 +274,18 @@ static int adc108s102_probe(struct spi_device *spi)
spi_message_init_with_transfers(&st->scan_single_msg, spi_message_init_with_transfers(&st->scan_single_msg,
&st->scan_single_xfer, 1); &st->scan_single_xfer, 1);
ret = iio_triggered_buffer_setup(indio_dev, NULL, ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
&adc108s102_trigger_handler, NULL); &adc108s102_trigger_handler,
NULL);
if (ret) if (ret)
goto error_disable_reg; return ret;
ret = iio_device_register(indio_dev); ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret) { if (ret)
dev_err(&spi->dev, "Failed to register IIO device\n"); dev_err(&spi->dev, "Failed to register IIO device\n");
goto error_cleanup_triggered_buffer;
}
return 0;
error_cleanup_triggered_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_disable_reg:
regulator_disable(st->reg);
return ret; return ret;
} }
static int adc108s102_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct adc108s102_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(st->reg);
return 0;
}
static const struct of_device_id adc108s102_of_match[] = { static const struct of_device_id adc108s102_of_match[] = {
{ .compatible = "ti,adc108s102" }, { .compatible = "ti,adc108s102" },
{ } { }
@ -327,7 +313,6 @@ static struct spi_driver adc108s102_driver = {
.acpi_match_table = ACPI_PTR(adc108s102_acpi_ids), .acpi_match_table = ACPI_PTR(adc108s102_acpi_ids),
}, },
.probe = adc108s102_probe, .probe = adc108s102_probe,
.remove = adc108s102_remove,
.id_table = adc108s102_id, .id_table = adc108s102_id,
}; };
module_spi_driver(adc108s102_driver); module_spi_driver(adc108s102_driver);

View File

@ -169,6 +169,11 @@ static const struct iio_info ti_adc_info = {
.read_raw = ti_adc_read_raw, .read_raw = ti_adc_read_raw,
}; };
static void ti_adc_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int ti_adc_probe(struct spi_device *spi) static int ti_adc_probe(struct spi_device *spi)
{ {
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
@ -182,7 +187,6 @@ static int ti_adc_probe(struct spi_device *spi)
indio_dev->info = &ti_adc_info; indio_dev->info = &ti_adc_info;
indio_dev->name = TI_ADC_DRV_NAME; indio_dev->name = TI_ADC_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
spi_set_drvdata(spi, indio_dev);
data = iio_priv(indio_dev); data = iio_priv(indio_dev);
data->spi = spi; data->spi = spi;
@ -203,42 +207,24 @@ static int ti_adc_probe(struct spi_device *spi)
} }
data->ref = devm_regulator_get(&spi->dev, "vdda"); data->ref = devm_regulator_get(&spi->dev, "vdda");
if (!IS_ERR(data->ref)) { if (IS_ERR(data->ref))
ret = regulator_enable(data->ref); return PTR_ERR(data->ref);
if (ret < 0)
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL, ret = regulator_enable(data->ref);
ti_adc_trigger_handler, NULL); if (ret < 0)
return ret;
ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable,
data->ref);
if (ret) if (ret)
goto error_regulator_disable; return ret;
ret = iio_device_register(indio_dev); ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
ti_adc_trigger_handler, NULL);
if (ret) if (ret)
goto error_unreg_buffer; return ret;
return 0; return devm_iio_device_register(&spi->dev, indio_dev);
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_regulator_disable:
regulator_disable(data->ref);
return ret;
}
static int ti_adc_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ti_adc_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(data->ref);
return 0;
} }
static const struct of_device_id ti_adc_dt_ids[] = { static const struct of_device_id ti_adc_dt_ids[] = {
@ -261,7 +247,6 @@ static struct spi_driver ti_adc_driver = {
.of_match_table = ti_adc_dt_ids, .of_match_table = ti_adc_dt_ids,
}, },
.probe = ti_adc_probe, .probe = ti_adc_probe,
.remove = ti_adc_remove,
.id_table = ti_adc_id, .id_table = ti_adc_id,
}; };
module_spi_driver(ti_adc_driver); module_spi_driver(ti_adc_driver);

View File

@ -395,10 +395,14 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p; struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev; struct iio_dev *indio_dev = pf->indio_dev;
struct ads1015_data *data = iio_priv(indio_dev); struct ads1015_data *data = iio_priv(indio_dev);
s16 buf[8]; /* 1x s16 ADC val + 3x s16 padding + 4x s16 timestamp */ /* Ensure natural alignment of timestamp */
struct {
s16 chan;
s64 timestamp __aligned(8);
} scan;
int chan, ret, res; int chan, ret, res;
memset(buf, 0, sizeof(buf)); memset(&scan, 0, sizeof(scan));
mutex_lock(&data->lock); mutex_lock(&data->lock);
chan = find_first_bit(indio_dev->active_scan_mask, chan = find_first_bit(indio_dev->active_scan_mask,
@ -409,10 +413,10 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p)
goto err; goto err;
} }
buf[0] = res; scan.chan = res;
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_push_to_buffers_with_timestamp(indio_dev, &scan,
iio_get_time_ns(indio_dev)); iio_get_time_ns(indio_dev));
err: err:

View File

@ -830,7 +830,6 @@ static int ads131e08_probe(struct spi_device *spi)
return ret; return ret;
indio_dev->name = st->info->name; indio_dev->name = st->info->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->info = &ads131e08_iio_info; indio_dev->info = &ads131e08_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
@ -850,7 +849,7 @@ static int ads131e08_probe(struct spi_device *spi)
} }
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
indio_dev->name, indio_dev->id); indio_dev->name, iio_device_id(indio_dev));
if (!st->trig) { if (!st->trig) {
dev_err(&spi->dev, "failed to allocate IIO trigger\n"); dev_err(&spi->dev, "failed to allocate IIO trigger\n");
return -ENOMEM; return -ENOMEM;

View File

@ -0,0 +1,714 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Texas Instruments TSC2046 SPI ADC driver
*
* Copyright (c) 2021 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <asm/unaligned.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger.h>
/*
* The PENIRQ of TSC2046 controller is implemented as level shifter attached to
* the X+ line. If voltage of the X+ line reaches a specific level the IRQ will
* be activated or deactivated.
* To make this kind of IRQ reusable as trigger following additions were
* implemented:
* - rate limiting:
* For typical touchscreen use case, we need to trigger about each 10ms.
* - hrtimer:
* Continue triggering at least once after the IRQ was deactivated. Then
* deactivate this trigger to stop sampling in order to reduce power
* consumption.
*/
#define TI_TSC2046_NAME "tsc2046"
/* This driver doesn't aim at the peak continuous sample rate */
#define TI_TSC2046_MAX_SAMPLE_RATE 125000
#define TI_TSC2046_SAMPLE_BITS \
BITS_PER_TYPE(struct tsc2046_adc_atom)
#define TI_TSC2046_MAX_CLK_FREQ \
(TI_TSC2046_MAX_SAMPLE_RATE * TI_TSC2046_SAMPLE_BITS)
#define TI_TSC2046_SAMPLE_INTERVAL_US 10000
#define TI_TSC2046_START BIT(7)
#define TI_TSC2046_ADDR GENMASK(6, 4)
#define TI_TSC2046_ADDR_TEMP1 7
#define TI_TSC2046_ADDR_AUX 6
#define TI_TSC2046_ADDR_X 5
#define TI_TSC2046_ADDR_Z2 4
#define TI_TSC2046_ADDR_Z1 3
#define TI_TSC2046_ADDR_VBAT 2
#define TI_TSC2046_ADDR_Y 1
#define TI_TSC2046_ADDR_TEMP0 0
/*
* The mode bit sets the resolution of the ADC. With this bit low, the next
* conversion has 12-bit resolution, whereas with this bit high, the next
* conversion has 8-bit resolution. This driver is optimized for 12-bit mode.
* So, for this driver, this bit should stay zero.
*/
#define TI_TSC2046_8BIT_MODE BIT(3)
/*
* SER/DFR - The SER/DFR bit controls the reference mode, either single-ended
* (high) or differential (low).
*/
#define TI_TSC2046_SER BIT(2)
/*
* If VREF_ON and ADC_ON are both zero, then the chip operates in
* auto-wake/suspend mode. In most case this bits should stay zero.
*/
#define TI_TSC2046_PD1_VREF_ON BIT(1)
#define TI_TSC2046_PD0_ADC_ON BIT(0)
/*
* All supported devices can do 8 or 12bit resolution. This driver
* supports only 12bit mode, here we have a 16bit data transfer, where
* the MSB and the 3 LSB are 0.
*/
#define TI_TSC2046_DATA_12BIT GENMASK(14, 3)
#define TI_TSC2046_MAX_CHAN 8
/* Represents a HW sample */
struct tsc2046_adc_atom {
/*
* Command transmitted to the controller. This field is empty on the RX
* buffer.
*/
u8 cmd;
/*
* Data received from the controller. This field is empty for the TX
* buffer
*/
__be16 data;
} __packed;
/* Layout of atomic buffers within big buffer */
struct tsc2046_adc_group_layout {
/* Group offset within the SPI RX buffer */
unsigned int offset;
/*
* Amount of tsc2046_adc_atom structs within the same command gathered
* within same group.
*/
unsigned int count;
/*
* Settling samples (tsc2046_adc_atom structs) which should be skipped
* before good samples will start.
*/
unsigned int skip;
};
struct tsc2046_adc_dcfg {
const struct iio_chan_spec *channels;
unsigned int num_channels;
};
struct tsc2046_adc_ch_cfg {
unsigned int settling_time_us;
unsigned int oversampling_ratio;
};
struct tsc2046_adc_priv {
struct spi_device *spi;
const struct tsc2046_adc_dcfg *dcfg;
struct iio_trigger *trig;
struct hrtimer trig_timer;
spinlock_t trig_lock;
unsigned int trig_more_count;
struct spi_transfer xfer;
struct spi_message msg;
struct {
/* Scan data for each channel */
u16 data[TI_TSC2046_MAX_CHAN];
/* Timestamp */
s64 ts __aligned(8);
} scan_buf;
/*
* Lock to protect the layout and the SPI transfer buffer.
* tsc2046_adc_group_layout can be changed within update_scan_mode(),
* in this case the l[] and tx/rx buffer will be out of sync to each
* other.
*/
struct mutex slock;
struct tsc2046_adc_group_layout l[TI_TSC2046_MAX_CHAN];
struct tsc2046_adc_atom *rx;
struct tsc2046_adc_atom *tx;
struct tsc2046_adc_atom *rx_one;
struct tsc2046_adc_atom *tx_one;
unsigned int count;
unsigned int groups;
u32 effective_speed_hz;
u32 scan_interval_us;
u32 time_per_scan_us;
u32 time_per_bit_ns;
struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN];
};
#define TI_TSC2046_V_CHAN(index, bits, name) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = index, \
.datasheet_name = "#name", \
.scan_index = index, \
.scan_type = { \
.sign = 'u', \
.realbits = bits, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
#define DECLARE_TI_TSC2046_8_CHANNELS(name, bits) \
const struct iio_chan_spec name ## _channels[] = { \
TI_TSC2046_V_CHAN(0, bits, TEMP0), \
TI_TSC2046_V_CHAN(1, bits, Y), \
TI_TSC2046_V_CHAN(2, bits, VBAT), \
TI_TSC2046_V_CHAN(3, bits, Z1), \
TI_TSC2046_V_CHAN(4, bits, Z2), \
TI_TSC2046_V_CHAN(5, bits, X), \
TI_TSC2046_V_CHAN(6, bits, AUX), \
TI_TSC2046_V_CHAN(7, bits, TEMP1), \
IIO_CHAN_SOFT_TIMESTAMP(8), \
}
static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12);
static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = {
.channels = tsc2046_adc_channels,
.num_channels = ARRAY_SIZE(tsc2046_adc_channels),
};
/*
* Convert time to a number of samples which can be transferred within this
* time.
*/
static unsigned int tsc2046_adc_time_to_count(struct tsc2046_adc_priv *priv,
unsigned long time)
{
unsigned int bit_count, sample_count;
bit_count = DIV_ROUND_UP(time * NSEC_PER_USEC, priv->time_per_bit_ns);
sample_count = DIV_ROUND_UP(bit_count, TI_TSC2046_SAMPLE_BITS);
dev_dbg(&priv->spi->dev, "Effective speed %u, time per bit: %u, count bits: %u, count samples: %u\n",
priv->effective_speed_hz, priv->time_per_bit_ns,
bit_count, sample_count);
return sample_count;
}
static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx,
bool keep_power)
{
u32 pd;
/*
* if PD bits are 0, controller will automatically disable ADC, VREF and
* enable IRQ.
*/
if (keep_power)
pd = TI_TSC2046_PD0_ADC_ON;
else
pd = 0;
return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd;
}
static u16 tsc2046_adc_get_value(struct tsc2046_adc_atom *buf)
{
return FIELD_GET(TI_TSC2046_DATA_12BIT, get_unaligned_be16(&buf->data));
}
static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
u32 *effective_speed_hz)
{
struct spi_transfer xfer;
struct spi_message msg;
int ret;
memset(&xfer, 0, sizeof(xfer));
priv->tx_one->cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
priv->tx_one->data = 0;
xfer.tx_buf = priv->tx_one;
xfer.rx_buf = priv->rx_one;
xfer.len = sizeof(*priv->tx_one);
spi_message_init_with_transfers(&msg, &xfer, 1);
/*
* We aren't using spi_write_then_read() because we need to be able
* to get hold of the effective_speed_hz from the xfer
*/
ret = spi_sync(priv->spi, &msg);
if (ret) {
dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n",
ERR_PTR(ret));
return ret;
}
if (effective_speed_hz)
*effective_speed_hz = xfer.effective_speed_hz;
return tsc2046_adc_get_value(priv->rx_one);
}
static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv,
unsigned int group,
unsigned int ch_idx)
{
struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx];
struct tsc2046_adc_group_layout *cur;
unsigned int max_count, count_skip;
unsigned int offset = 0;
if (group)
offset = priv->l[group - 1].offset + priv->l[group - 1].count;
count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us);
max_count = count_skip + ch->oversampling_ratio;
cur = &priv->l[group];
cur->offset = offset;
cur->count = max_count;
cur->skip = count_skip;
return sizeof(*priv->tx) * max_count;
}
static void tsc2046_adc_group_set_cmd(struct tsc2046_adc_priv *priv,
unsigned int group, int ch_idx)
{
struct tsc2046_adc_group_layout *l = &priv->l[group];
unsigned int i;
u8 cmd;
/*
* Do not enable automatic power down on working samples. Otherwise the
* plates will never be completely charged.
*/
cmd = tsc2046_adc_get_cmd(priv, ch_idx, true);
for (i = 0; i < l->count - 1; i++)
priv->tx[l->offset + i].cmd = cmd;
/* automatically power down on last sample */
priv->tx[l->offset + i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
}
static u16 tsc2046_adc_get_val(struct tsc2046_adc_priv *priv, int group)
{
struct tsc2046_adc_group_layout *l;
unsigned int val, val_normalized = 0;
int valid_count, i;
l = &priv->l[group];
valid_count = l->count - l->skip;
for (i = 0; i < valid_count; i++) {
val = tsc2046_adc_get_value(&priv->rx[l->offset + l->skip + i]);
val_normalized += val;
}
return DIV_ROUND_UP(val_normalized, valid_count);
}
static int tsc2046_adc_scan(struct iio_dev *indio_dev)
{
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
struct device *dev = &priv->spi->dev;
int group;
int ret;
ret = spi_sync(priv->spi, &priv->msg);
if (ret < 0) {
dev_err_ratelimited(dev, "SPI transfer failed: %pe\n", ERR_PTR(ret));
return ret;
}
for (group = 0; group < priv->groups; group++)
priv->scan_buf.data[group] = tsc2046_adc_get_val(priv, group);
ret = iio_push_to_buffers_with_timestamp(indio_dev, &priv->scan_buf,
iio_get_time_ns(indio_dev));
/* If the consumer is kfifo, we may get a EBUSY here - ignore it. */
if (ret < 0 && ret != -EBUSY) {
dev_err_ratelimited(dev, "Failed to push scan buffer %pe\n",
ERR_PTR(ret));
return ret;
}
return 0;
}
static irqreturn_t tsc2046_adc_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
mutex_lock(&priv->slock);
tsc2046_adc_scan(indio_dev);
mutex_unlock(&priv->slock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *active_scan_mask)
{
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
unsigned int ch_idx, group = 0;
size_t size;
mutex_lock(&priv->slock);
size = 0;
for_each_set_bit(ch_idx, active_scan_mask, indio_dev->num_channels) {
size += tsc2046_adc_group_set_layout(priv, group, ch_idx);
tsc2046_adc_group_set_cmd(priv, group, ch_idx);
group++;
}
priv->groups = group;
priv->xfer.len = size;
priv->time_per_scan_us = size * 8 * priv->time_per_bit_ns / NSEC_PER_USEC;
if (priv->scan_interval_us > priv->time_per_scan_us)
dev_warn(&priv->spi->dev, "The scan interval (%d) is less then calculated scan time (%d)\n",
priv->scan_interval_us, priv->time_per_scan_us);
mutex_unlock(&priv->slock);
return 0;
}
static const struct iio_info tsc2046_adc_info = {
.update_scan_mode = tsc2046_adc_update_scan_mode,
};
static enum hrtimer_restart tsc2046_adc_trig_more(struct hrtimer *hrtimer)
{
struct tsc2046_adc_priv *priv = container_of(hrtimer,
struct tsc2046_adc_priv,
trig_timer);
unsigned long flags;
spin_lock_irqsave(&priv->trig_lock, flags);
disable_irq_nosync(priv->spi->irq);
priv->trig_more_count++;
iio_trigger_poll(priv->trig);
spin_unlock_irqrestore(&priv->trig_lock, flags);
return HRTIMER_NORESTART;
}
static irqreturn_t tsc2046_adc_irq(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
spin_lock(&priv->trig_lock);
hrtimer_try_to_cancel(&priv->trig_timer);
priv->trig_more_count = 0;
disable_irq_nosync(priv->spi->irq);
iio_trigger_poll(priv->trig);
spin_unlock(&priv->trig_lock);
return IRQ_HANDLED;
}
static void tsc2046_adc_reenable_trigger(struct iio_trigger *trig)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
unsigned long flags;
int delta;
/*
* We can sample it as fast as we can, but usually we do not need so
* many samples. Reduce the sample rate for default (touchscreen) use
* case.
* Currently we do not need a highly precise sample rate. It is enough
* to have calculated numbers.
*/
delta = priv->scan_interval_us - priv->time_per_scan_us;
if (delta > 0)
fsleep(delta);
spin_lock_irqsave(&priv->trig_lock, flags);
/*
* We need to trigger at least one extra sample to detect state
* difference on ADC side.
*/
if (!priv->trig_more_count) {
int timeout_ms = DIV_ROUND_UP(priv->scan_interval_us,
USEC_PER_MSEC);
hrtimer_start(&priv->trig_timer, ms_to_ktime(timeout_ms),
HRTIMER_MODE_REL_SOFT);
}
enable_irq(priv->spi->irq);
spin_unlock_irqrestore(&priv->trig_lock, flags);
}
static int tsc2046_adc_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
if (enable) {
enable_irq(priv->spi->irq);
} else {
disable_irq(priv->spi->irq);
hrtimer_try_to_cancel(&priv->trig_timer);
}
return 0;
}
static const struct iio_trigger_ops tsc2046_adc_trigger_ops = {
.set_trigger_state = tsc2046_adc_set_trigger_state,
.reenable = tsc2046_adc_reenable_trigger,
};
static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv)
{
unsigned int ch_idx;
size_t size;
int ret;
priv->tx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->tx_one),
GFP_KERNEL);
if (!priv->tx_one)
return -ENOMEM;
priv->rx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->rx_one),
GFP_KERNEL);
if (!priv->rx_one)
return -ENOMEM;
/*
* Make dummy read to set initial power state and get real SPI clock
* freq. It seems to be not important which channel is used for this
* case.
*/
ret = tsc2046_adc_read_one(priv, TI_TSC2046_ADDR_TEMP0,
&priv->effective_speed_hz);
if (ret < 0)
return ret;
/*
* In case SPI controller do not report effective_speed_hz, use
* configure value and hope it will match.
*/
if (!priv->effective_speed_hz)
priv->effective_speed_hz = priv->spi->max_speed_hz;
priv->scan_interval_us = TI_TSC2046_SAMPLE_INTERVAL_US;
priv->time_per_bit_ns = DIV_ROUND_UP(NSEC_PER_SEC,
priv->effective_speed_hz);
/*
* Calculate and allocate maximal size buffer if all channels are
* enabled.
*/
size = 0;
for (ch_idx = 0; ch_idx < priv->dcfg->num_channels; ch_idx++)
size += tsc2046_adc_group_set_layout(priv, ch_idx, ch_idx);
priv->tx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL);
if (!priv->tx)
return -ENOMEM;
priv->rx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL);
if (!priv->rx)
return -ENOMEM;
priv->xfer.tx_buf = priv->tx;
priv->xfer.rx_buf = priv->rx;
priv->xfer.len = size;
spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
return 0;
}
static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv)
{
struct fwnode_handle *child;
struct device *dev = &priv->spi->dev;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(priv->ch_cfg); i++) {
priv->ch_cfg[i].settling_time_us = 1;
priv->ch_cfg[i].oversampling_ratio = 1;
}
device_for_each_child_node(dev, child) {
u32 stl, overs, reg;
int ret;
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret) {
dev_err(dev, "invalid reg on %pfw, err: %pe\n", child,
ERR_PTR(ret));
continue;
}
if (reg >= ARRAY_SIZE(priv->ch_cfg)) {
dev_err(dev, "%pfw: Unsupported reg value: %i, max supported is: %zu.\n",
child, reg, ARRAY_SIZE(priv->ch_cfg));
continue;
}
ret = fwnode_property_read_u32(child, "settling-time-us", &stl);
if (!ret)
priv->ch_cfg[reg].settling_time_us = stl;
ret = fwnode_property_read_u32(child, "oversampling-ratio",
&overs);
if (!ret)
priv->ch_cfg[reg].oversampling_ratio = overs;
}
}
static int tsc2046_adc_probe(struct spi_device *spi)
{
const struct tsc2046_adc_dcfg *dcfg;
struct device *dev = &spi->dev;
struct tsc2046_adc_priv *priv;
struct iio_dev *indio_dev;
struct iio_trigger *trig;
int ret;
if (spi->max_speed_hz > TI_TSC2046_MAX_CLK_FREQ) {
dev_err(dev, "SPI max_speed_hz is too high: %d Hz. Max supported freq is %zu Hz\n",
spi->max_speed_hz, TI_TSC2046_MAX_CLK_FREQ);
return -EINVAL;
}
dcfg = device_get_match_data(dev);
if (!dcfg)
return -EINVAL;
spi->bits_per_word = 8;
spi->mode &= ~SPI_MODE_X_MASK;
spi->mode |= SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0)
return dev_err_probe(dev, ret, "Error in SPI setup\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
priv->dcfg = dcfg;
spi_set_drvdata(spi, indio_dev);
priv->spi = spi;
indio_dev->name = TI_TSC2046_NAME;
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
indio_dev->channels = dcfg->channels;
indio_dev->num_channels = dcfg->num_channels;
indio_dev->info = &tsc2046_adc_info;
tsc2046_adc_parse_fwnode(priv);
ret = tsc2046_adc_setup_spi_msg(priv);
if (ret)
return ret;
mutex_init(&priv->slock);
ret = devm_request_irq(dev, spi->irq, &tsc2046_adc_irq,
IRQF_NO_AUTOEN, indio_dev->name, indio_dev);
if (ret)
return ret;
trig = devm_iio_trigger_alloc(dev, "touchscreen-%s", indio_dev->name);
if (!trig)
return -ENOMEM;
priv->trig = trig;
iio_trigger_set_drvdata(trig, indio_dev);
trig->ops = &tsc2046_adc_trigger_ops;
spin_lock_init(&priv->trig_lock);
hrtimer_init(&priv->trig_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
priv->trig_timer.function = tsc2046_adc_trig_more;
ret = devm_iio_trigger_register(dev, trig);
if (ret) {
dev_err(dev, "failed to register trigger\n");
return ret;
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
&tsc2046_adc_trigger_handler, NULL);
if (ret) {
dev_err(dev, "Failed to setup triggered buffer\n");
return ret;
}
/* set default trigger */
indio_dev->trig = iio_trigger_get(priv->trig);
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id ads7950_of_table[] = {
{ .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e },
{ }
};
MODULE_DEVICE_TABLE(of, ads7950_of_table);
static struct spi_driver tsc2046_adc_driver = {
.driver = {
.name = "tsc2046",
.of_match_table = ads7950_of_table,
},
.probe = tsc2046_adc_probe,
};
module_spi_driver(tsc2046_adc_driver);
MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
MODULE_DESCRIPTION("TI TSC2046 ADC");
MODULE_LICENSE("GPL v2");

View File

@ -167,7 +167,11 @@ struct vf610_adc {
u32 sample_freq_avail[5]; u32 sample_freq_avail[5];
struct completion completion; struct completion completion;
u16 buffer[8]; /* Ensure the timestamp is naturally aligned */
struct {
u16 chan;
s64 timestamp __aligned(8);
} scan;
}; };
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
@ -579,9 +583,9 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
if (coco & VF610_ADC_HS_COCO0) { if (coco & VF610_ADC_HS_COCO0) {
info->value = vf610_adc_read_data(info); info->value = vf610_adc_read_data(info);
if (iio_buffer_enabled(indio_dev)) { if (iio_buffer_enabled(indio_dev)) {
info->buffer[0] = info->value; info->scan.chan = info->value;
iio_push_to_buffers_with_timestamp(indio_dev, iio_push_to_buffers_with_timestamp(indio_dev,
info->buffer, &info->scan,
iio_get_time_ns(indio_dev)); iio_get_time_ns(indio_dev));
iio_trigger_notify_done(indio_dev->trig); iio_trigger_notify_done(indio_dev->trig);
} else } else

View File

@ -743,7 +743,7 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
int ret; int ret;
trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name, trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name,
indio_dev->id, name); iio_device_id(indio_dev), name);
if (trig == NULL) if (trig == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);

View File

@ -29,6 +29,7 @@ struct rescale {
struct iio_channel *source; struct iio_channel *source;
struct iio_chan_spec chan; struct iio_chan_spec chan;
struct iio_chan_spec_ext_info *ext_info; struct iio_chan_spec_ext_info *ext_info;
bool chan_processed;
s32 numerator; s32 numerator;
s32 denominator; s32 denominator;
}; };
@ -43,10 +44,27 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
return iio_read_channel_raw(rescale->source, val); if (rescale->chan_processed)
/*
* When only processed channels are supported, we
* read the processed data and scale it by 1/1
* augmented with whatever the rescaler has calculated.
*/
return iio_read_channel_processed(rescale->source, val);
else
return iio_read_channel_raw(rescale->source, val);
case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_SCALE:
ret = iio_read_channel_scale(rescale->source, val, val2); if (rescale->chan_processed) {
/*
* Processed channels are scaled 1-to-1
*/
*val = 1;
*val2 = 1;
ret = IIO_VAL_FRACTIONAL;
} else {
ret = iio_read_channel_scale(rescale->source, val, val2);
}
switch (ret) { switch (ret) {
case IIO_VAL_FRACTIONAL: case IIO_VAL_FRACTIONAL:
*val *= rescale->numerator; *val *= rescale->numerator;
@ -130,16 +148,27 @@ static int rescale_configure_channel(struct device *dev,
chan->ext_info = rescale->ext_info; chan->ext_info = rescale->ext_info;
chan->type = rescale->cfg->type; chan->type = rescale->cfg->type;
if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) || if (iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) ||
!iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) { iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) {
dev_err(dev, "source channel does not support raw/scale\n"); dev_info(dev, "using raw+scale source channel\n");
} else if (iio_channel_has_info(schan, IIO_CHAN_INFO_PROCESSED)) {
dev_info(dev, "using processed channel\n");
rescale->chan_processed = true;
} else {
dev_err(dev, "source channel is not supported\n");
return -EINVAL; return -EINVAL;
} }
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE); BIT(IIO_CHAN_INFO_SCALE);
if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW)) /*
* Using .read_avail() is fringe to begin with and makes no sense
* whatsoever for processed channels, so we make sure that this cannot
* be called on a processed channel.
*/
if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW) &&
!rescale->chan_processed)
chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
return 0; return 0;

View File

@ -228,9 +228,9 @@ static void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
iio_buffer_put(buffer); iio_buffer_put(buffer);
} }
static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res) static void __devm_iio_dmaengine_buffer_free(void *buffer)
{ {
iio_dmaengine_buffer_free(*(struct iio_buffer **)res); iio_dmaengine_buffer_free(buffer);
} }
/** /**
@ -247,21 +247,17 @@ static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res)
static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel) const char *channel)
{ {
struct iio_buffer **bufferp, *buffer; struct iio_buffer *buffer;
int ret;
bufferp = devres_alloc(__devm_iio_dmaengine_buffer_free,
sizeof(*bufferp), GFP_KERNEL);
if (!bufferp)
return ERR_PTR(-ENOMEM);
buffer = iio_dmaengine_buffer_alloc(dev, channel); buffer = iio_dmaengine_buffer_alloc(dev, channel);
if (IS_ERR(buffer)) { if (IS_ERR(buffer))
devres_free(bufferp);
return buffer; return buffer;
}
*bufferp = buffer; ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
devres_add(dev, bufferp); buffer);
if (ret)
return ERR_PTR(ret);
return buffer; return buffer;
} }

View File

@ -137,9 +137,9 @@ void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
} }
EXPORT_SYMBOL_GPL(iio_hw_consumer_free); EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
static void devm_iio_hw_consumer_release(struct device *dev, void *res) static void devm_iio_hw_consumer_release(void *iio_hwc)
{ {
iio_hw_consumer_free(*(struct iio_hw_consumer **)res); iio_hw_consumer_free(iio_hwc);
} }
/** /**
@ -153,20 +153,17 @@ static void devm_iio_hw_consumer_release(struct device *dev, void *res)
*/ */
struct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev) struct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev)
{ {
struct iio_hw_consumer **ptr, *iio_hwc; struct iio_hw_consumer *iio_hwc;
int ret;
ptr = devres_alloc(devm_iio_hw_consumer_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return NULL;
iio_hwc = iio_hw_consumer_alloc(dev); iio_hwc = iio_hw_consumer_alloc(dev);
if (IS_ERR(iio_hwc)) { if (IS_ERR(iio_hwc))
devres_free(ptr); return iio_hwc;
} else {
*ptr = iio_hwc; ret = devm_add_action_or_reset(dev, devm_iio_hw_consumer_release,
devres_add(dev, ptr); iio_hwc);
} if (ret)
return ERR_PTR(ret);
return iio_hwc; return iio_hwc;
} }

View File

@ -56,7 +56,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
indio_dev, indio_dev,
"%s_consumer%d", "%s_consumer%d",
indio_dev->name, indio_dev->name,
indio_dev->id); iio_device_id(indio_dev));
if (indio_dev->pollfunc == NULL) { if (indio_dev->pollfunc == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto error_kfifo_free; goto error_kfifo_free;
@ -96,9 +96,9 @@ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
} }
EXPORT_SYMBOL(iio_triggered_buffer_cleanup); EXPORT_SYMBOL(iio_triggered_buffer_cleanup);
static void devm_iio_triggered_buffer_clean(struct device *dev, void *res) static void devm_iio_triggered_buffer_clean(void *indio_dev)
{ {
iio_triggered_buffer_cleanup(*(struct iio_dev **)res); iio_triggered_buffer_cleanup(indio_dev);
} }
int devm_iio_triggered_buffer_setup_ext(struct device *dev, int devm_iio_triggered_buffer_setup_ext(struct device *dev,
@ -108,24 +108,15 @@ int devm_iio_triggered_buffer_setup_ext(struct device *dev,
const struct iio_buffer_setup_ops *ops, const struct iio_buffer_setup_ops *ops,
const struct attribute **buffer_attrs) const struct attribute **buffer_attrs)
{ {
struct iio_dev **ptr;
int ret; int ret;
ptr = devres_alloc(devm_iio_triggered_buffer_clean, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return -ENOMEM;
*ptr = indio_dev;
ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, ops, ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, ops,
buffer_attrs); buffer_attrs);
if (!ret) if (ret)
devres_add(dev, ptr); return ret;
else
devres_free(ptr);
return ret; return devm_add_action_or_reset(dev, devm_iio_triggered_buffer_clean,
indio_dev);
} }
EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_setup_ext); EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_setup_ext);

View File

@ -132,17 +132,32 @@ config SENSIRION_SGP30
module will be called sgp30. module will be called sgp30.
config SPS30 config SPS30
tristate "SPS30 particulate matter sensor" tristate
depends on I2C
select CRC8
select IIO_BUFFER select IIO_BUFFER
select IIO_TRIGGERED_BUFFER select IIO_TRIGGERED_BUFFER
config SPS30_I2C
tristate "SPS30 particulate matter sensor I2C driver"
depends on I2C
select SPS30
select CRC8
help help
Say Y here to build support for the Sensirion SPS30 particulate Say Y here to build support for the Sensirion SPS30 I2C interface
matter sensor. driver.
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called sps30. be called sps30_i2c.
config SPS30_SERIAL
tristate "SPS30 particulate matter sensor serial driver"
depends on SERIAL_DEV_BUS
select SPS30
help
Say Y here to build support for the Sensirion SPS30 serial interface
driver.
To compile this driver as a module, choose M here: the module will
be called sps30_serial.
config VZ89X config VZ89X
tristate "SGX Sensortech MiCS VZ89X VOC sensor" tristate "SGX Sensortech MiCS VZ89X VOC sensor"

View File

@ -17,4 +17,6 @@ obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
obj-$(CONFIG_SPS30) += sps30.o obj-$(CONFIG_SPS30) += sps30.o
obj-$(CONFIG_SPS30_I2C) += sps30_i2c.o
obj-$(CONFIG_SPS30_SERIAL) += sps30_serial.o
obj-$(CONFIG_VZ89X) += vz89x.o obj-$(CONFIG_VZ89X) += vz89x.o

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