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:
commit
6771fb0b94
|
@ -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
|
|
@ -193,6 +193,15 @@ Description:
|
|||
both edges:
|
||||
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
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
|
|
@ -455,6 +455,19 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
Hardware applied calibration offset (assumed to fix production
|
||||
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_supply_calibscale
|
||||
|
@ -652,6 +665,25 @@ Description:
|
|||
Output frequency for channel Y in Hz. The number must always be
|
||||
specified and unique if the output corresponds to a single
|
||||
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
|
||||
KernelVersion: 3.4.0
|
||||
|
@ -663,6 +695,17 @@ Description:
|
|||
specified and unique if the output corresponds to a single
|
||||
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
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -1195,16 +1238,12 @@ Description:
|
|||
The name of the trigger source being used, as per string given
|
||||
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
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
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
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -1212,8 +1251,6 @@ Description:
|
|||
Actually start the buffer capture up. Will start trigger
|
||||
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
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -1224,34 +1261,6 @@ Description:
|
|||
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
|
||||
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_z_en
|
||||
|
@ -1284,23 +1293,6 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
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_anglvel_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,
|
||||
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_supply_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
|
||||
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
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -1633,8 +1596,6 @@ Description:
|
|||
the available samples after the timeout expires and thus have a
|
||||
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
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
|
|
@ -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
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -34,11 +9,3 @@ Description:
|
|||
out_altvoltage2_name: RF16x
|
||||
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.
|
||||
|
|
|
@ -18,6 +18,8 @@ Description:
|
|||
respectively which simply helper channels containing the
|
||||
calculated difference in the value of stage 1 - 2 and 3 - 4.
|
||||
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
|
||||
Date: May 2016
|
||||
|
@ -35,11 +37,3 @@ Contact: Andrew F. Davis <afd@ti.com>
|
|||
Description:
|
||||
Get and set the resistance and the capacitance settings for the
|
||||
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.
|
||||
|
|
|
@ -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).
|
|
@ -41,14 +41,6 @@ Description:
|
|||
Get the current light zone (0..4) as defined by the
|
||||
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
|
||||
Date: May 2012
|
||||
KernelVersion: 3.5
|
||||
|
@ -59,3 +51,6 @@ Description:
|
|||
|
||||
These values correspond to the ALS-mapper target registers for
|
||||
ALS-mapper Y + 1.
|
||||
|
||||
Note that out_currentY_raw provides the current for the
|
||||
current zone.
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
...
|
|
@ -16,6 +16,7 @@ properties:
|
|||
- kionix,kxcj91008
|
||||
- kionix,kxtj21009
|
||||
- kionix,kxtf9
|
||||
- kionix,kx023-1025
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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";
|
||||
};
|
||||
};
|
|
@ -39,4 +39,16 @@ properties:
|
|||
The first value specifies the positive input pin, the second
|
||||
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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -24,6 +24,9 @@ properties:
|
|||
description: |
|
||||
Channel node of a voltage io-channel.
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 0
|
||||
|
||||
shunt-resistor-micro-ohms:
|
||||
description: The shunt resistance.
|
||||
|
||||
|
@ -57,6 +60,7 @@ examples:
|
|||
sysi {
|
||||
compatible = "current-sense-shunt";
|
||||
io-channels = <&tiadc 0>;
|
||||
#io-channel-cells = <0>;
|
||||
|
||||
/* Divide the voltage by 3300000/1000000 (or 3.3) for the current. */
|
||||
shunt-resistor-micro-ohms = <3300000>;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -22,7 +22,6 @@ properties:
|
|||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -37,5 +36,11 @@ examples:
|
|||
reg = <0x69>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
serial {
|
||||
air-pollution-sensor {
|
||||
compatible = "sensirion,sps30";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -6,7 +6,9 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
|||
|
||||
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
|
||||
IMUs need their own bindings.
|
||||
The STMicroelectronics sensor devices are pretty straight-forward I2C or
|
||||
|
@ -15,90 +17,181 @@ description: |
|
|||
|
||||
maintainers:
|
||||
- Denis Ciocca <denis.ciocca@st.com>
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description: |
|
||||
Some values are deprecated.
|
||||
st,lis3lv02d (deprecated, use st,lis3lv02dl-accel)
|
||||
st,lis302dl-spi (deprecated, use st,lis3lv02dl-accel)
|
||||
enum:
|
||||
# Accelerometers
|
||||
- st,lis3lv02d
|
||||
- st,lis302dl-spi
|
||||
- st,lis3lv02dl-accel
|
||||
- st,lsm303dlh-accel
|
||||
- st,lsm303dlhc-accel
|
||||
- st,lis3dh-accel
|
||||
- st,lsm330d-accel
|
||||
- st,lsm330dl-accel
|
||||
- st,lsm330dlc-accel
|
||||
- st,lis331dl-accel
|
||||
- st,lis331dlh-accel
|
||||
- st,lsm303dl-accel
|
||||
- st,lsm303dlm-accel
|
||||
- st,lsm330-accel
|
||||
- st,lsm303agr-accel
|
||||
- st,lis2dh12-accel
|
||||
- st,h3lis331dl-accel
|
||||
- st,lng2dm-accel
|
||||
- st,lis3l02dq
|
||||
- st,lis2dw12
|
||||
- st,lis3dhh
|
||||
- st,lis3de
|
||||
- st,lis2de12
|
||||
- st,lis2hh12
|
||||
# Gyroscopes
|
||||
- st,l3g4200d-gyro
|
||||
- st,lsm330d-gyro
|
||||
- st,lsm330dl-gyro
|
||||
- st,lsm330dlc-gyro
|
||||
- st,l3gd20-gyro
|
||||
- st,l3gd20h-gyro
|
||||
- st,l3g4is-gyro
|
||||
- st,lsm330-gyro
|
||||
- st,lsm9ds0-gyro
|
||||
# Magnetometers
|
||||
- st,lsm303agr-magn
|
||||
- st,lsm303dlh-magn
|
||||
- st,lsm303dlhc-magn
|
||||
- st,lsm303dlm-magn
|
||||
- st,lis3mdl-magn
|
||||
- st,lis2mdl
|
||||
- st,lsm9ds1-magn
|
||||
- st,iis2mdc
|
||||
# Pressure sensors
|
||||
- st,lps001wp-press
|
||||
- st,lps25h-press
|
||||
- st,lps331ap-press
|
||||
- st,lps22hb-press
|
||||
- st,lps33hw
|
||||
- st,lps35hw
|
||||
- st,lps22hh
|
||||
oneOf:
|
||||
- description: STMicroelectronics Accelerometers
|
||||
enum:
|
||||
- st,h3lis331dl-accel
|
||||
- st,lis2de12
|
||||
- st,lis2dw12
|
||||
- st,lis2hh12
|
||||
- st,lis2dh12-accel
|
||||
- st,lis331dl-accel
|
||||
- st,lis331dlh-accel
|
||||
- st,lis3de
|
||||
- st,lis3dh-accel
|
||||
- st,lis3dhh
|
||||
- st,lis3l02dq
|
||||
- st,lis3lv02dl-accel
|
||||
- st,lng2dm-accel
|
||||
- st,lsm303agr-accel
|
||||
- st,lsm303dl-accel
|
||||
- st,lsm303dlh-accel
|
||||
- st,lsm303dlhc-accel
|
||||
- st,lsm303dlm-accel
|
||||
- st,lsm330-accel
|
||||
- st,lsm330d-accel
|
||||
- st,lsm330dl-accel
|
||||
- st,lsm330dlc-accel
|
||||
- description: STMicroelectronics Gyroscopes
|
||||
enum:
|
||||
- st,l3g4200d-gyro
|
||||
- st,l3g4is-gyro
|
||||
- st,l3gd20-gyro
|
||||
- st,l3gd20h-gyro
|
||||
- st,lsm330-gyro
|
||||
- st,lsm330d-gyro
|
||||
- st,lsm330dl-gyro
|
||||
- st,lsm330dlc-gyro
|
||||
- st,lsm9ds0-gyro
|
||||
- description: STMicroelectronics Magnetometers
|
||||
enum:
|
||||
- st,lis2mdl
|
||||
- st,lis3mdl-magn
|
||||
- st,lsm303agr-magn
|
||||
- st,lsm303dlh-magn
|
||||
- st,lsm303dlhc-magn
|
||||
- st,lsm303dlm-magn
|
||||
- st,lsm9ds1-magn
|
||||
- description: STMicroelectronics Pressure Sensors
|
||||
enum:
|
||||
- st,lps001wp-press
|
||||
- st,lps22hb-press
|
||||
- st,lps22hh
|
||||
- st,lps25h-press
|
||||
- st,lps331ap-press
|
||||
- st,lps33hw
|
||||
- st,lps35hw
|
||||
- description: IMUs
|
||||
enum:
|
||||
- st,lsm9ds0-imu
|
||||
- description: Deprecated bindings
|
||||
enum:
|
||||
- st,lis302dl-spi
|
||||
- st,lis3lv02d
|
||||
deprecated: true
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
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
|
||||
maxItems: 2
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
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
|
||||
description:
|
||||
Some sensors have multiple possible pins via which they can provide
|
||||
a data ready interrupt. This selects which one.
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
enum: [1, 2]
|
||||
|
||||
drive-open-drain:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: |
|
||||
The interrupt/data ready line will be configured as open drain, which
|
||||
is useful if several sensors share the same interrupt line.
|
||||
description: the interrupt/data ready line will be configured
|
||||
as open drain, which is useful if several sensors share the same
|
||||
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:
|
||||
- compatible
|
||||
|
@ -110,15 +203,30 @@ examples:
|
|||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
accelerometer@1d {
|
||||
compatible = "st,lis3lv02dl-accel";
|
||||
reg = <0x1d>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <18 IRQ_TYPE_EDGE_RISING>;
|
||||
pinctrl-0 = <&lis3lv02dl_nhk_mode>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
accelerometer@1c {
|
||||
compatible = "st,lis331dl-accel";
|
||||
reg = <0x1c>;
|
||||
st,drdy-int-pin = <1>;
|
||||
vdd-supply = <&ldo1>;
|
||||
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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -173,8 +173,14 @@ properties:
|
|||
- meas,tsys01
|
||||
# MEMSIC magnetometer
|
||||
- memsic,mmc35240
|
||||
# MEMSIC 3-axis accelerometer
|
||||
- memsic,mx4005
|
||||
# MEMSIC 2-axis 8-bit digital accelerometer
|
||||
- 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,mcp3421
|
||||
# Microchip differential I2C ADC, 2 Channel, 18 bit
|
||||
|
@ -259,6 +265,10 @@ properties:
|
|||
- sensirion,sgpc3
|
||||
# Sensirion multi-pixel gas sensor with I2C interface
|
||||
- sensirion,sgp30
|
||||
# Sensortek 3 axis accelerometer
|
||||
- sensortek,stk8312
|
||||
# Sensortek 3 axis accelerometer
|
||||
- sensortek,stk8ba50
|
||||
# SGX Sensortech VZ89X Sensors
|
||||
- sgx,vz89x
|
||||
# Relative Humidity and Temperature Sensors
|
||||
|
|
22
MAINTAINERS
22
MAINTAINERS
|
@ -9371,6 +9371,11 @@ L: linux-pm@vger.kernel.org
|
|||
S: Supported
|
||||
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
|
||||
M: Faisal Latif <faisal.latif@intel.com>
|
||||
M: Shiraz Saleem <shiraz.saleem@intel.com>
|
||||
|
@ -16494,6 +16499,8 @@ M: Tomasz Duszynski <tduszyns@gmail.com>
|
|||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml
|
||||
F: drivers/iio/chemical/sps30.c
|
||||
F: drivers/iio/chemical/sps30_i2c.c
|
||||
F: drivers/iio/chemical/sps30_serial.c
|
||||
|
||||
SERIAL DEVICE BUS
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
|
@ -18109,6 +18116,13 @@ F: Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
|
|||
F: Documentation/hwmon/tps23861.rst
|
||||
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
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -18338,6 +18352,14 @@ S: Supported
|
|||
F: Documentation/devicetree/bindings/net/nfc/trf7970a.txt
|
||||
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
|
||||
M: Peter Ujfalusi <peter.ujfalusi@gmail.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
|
|
@ -91,4 +91,14 @@ config MICROCHIP_TCB_CAPTURE
|
|||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
|
|
|
@ -12,3 +12,4 @@ obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
|
|||
obj-$(CONFIG_TI_EQEP) += ti-eqep.o
|
||||
obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
|
||||
obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o
|
||||
obj-$(CONFIG_INTEL_QEP) += intel-qep.o
|
||||
|
|
|
@ -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");
|
|
@ -226,6 +226,33 @@ config DMARD10
|
|||
Choosing M will build the driver as a module. If so, the module
|
||||
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
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
@ -449,6 +476,19 @@ config SCA3000
|
|||
To compile this driver as a module, say M here: the module will be
|
||||
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
|
||||
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
|
|
|
@ -27,6 +27,9 @@ obj-$(CONFIG_DA311) += da311.o
|
|||
obj-$(CONFIG_DMARD06) += dmard06.o
|
||||
obj-$(CONFIG_DMARD09) += dmard09.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_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
@ -50,6 +53,7 @@ obj-$(CONFIG_MXC4005) += mxc4005.o
|
|||
obj-$(CONFIG_MXC6255) += mxc6255.o
|
||||
|
||||
obj-$(CONFIG_SCA3000) += sca3000.o
|
||||
obj-$(CONFIG_SCA3300) += sca3300.o
|
||||
|
||||
obj-$(CONFIG_STK8312) += stk8312.o
|
||||
obj-$(CONFIG_STK8BA50) += stk8ba50.o
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
|
|
@ -7,11 +7,8 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
|
|
@ -1223,14 +1223,14 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
|
|||
st->dready_trig = devm_iio_trigger_alloc(dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (st->dready_trig == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st->peak_datardy_trig = devm_iio_trigger_alloc(dev,
|
||||
"%s-dev%d-peak",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!st->peak_datardy_trig)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -162,7 +162,11 @@ struct bma180_data {
|
|||
int scale;
|
||||
int bw;
|
||||
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 {
|
||||
|
@ -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_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,
|
||||
0, 0, 306458 };
|
||||
|
||||
|
@ -938,12 +942,12 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
|
|||
mutex_unlock(&data->mutex);
|
||||
goto err;
|
||||
}
|
||||
((s16 *)data->buff)[i++] = ret;
|
||||
data->scan.chan[i++] = ret;
|
||||
}
|
||||
|
||||
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:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
|
@ -997,8 +1001,7 @@ static int bma180_probe(struct i2c_client *client,
|
|||
chip = id->driver_data;
|
||||
data->part_info = &bma180_part_info[chip];
|
||||
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
ret = iio_read_mount_matrix(dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1045,7 +1048,7 @@ static int bma180_probe(struct i2c_client *client,
|
|||
|
||||
if (client->irq > 0) {
|
||||
data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!data->trig) {
|
||||
ret = -ENOMEM;
|
||||
goto err_chip_disable;
|
||||
|
|
|
@ -63,7 +63,11 @@ static const int bma220_scale_table[][2] = {
|
|||
struct bma220_data {
|
||||
struct spi_device *spi_device;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -94,12 +98,12 @@ static irqreturn_t bma220_trigger_handler(int irq, void *p)
|
|||
|
||||
mutex_lock(&data->lock);
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
|
||||
pf->timestamp);
|
||||
err:
|
||||
mutex_unlock(&data->lock);
|
||||
|
|
|
@ -811,7 +811,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation);
|
||||
ret = iio_read_mount_matrix(dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -389,7 +389,7 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
|
|||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(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) {
|
||||
dev_err(dev,
|
||||
"Failed: %s for %d\n", __func__, on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
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];
|
||||
|
||||
t->indio_trig = devm_iio_trigger_alloc(dev,
|
||||
bmc150_accel_triggers[i].name,
|
||||
bmc150_accel_triggers[i].name,
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!t->indio_trig) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
|
@ -1688,8 +1685,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
data->regmap = regmap;
|
||||
|
||||
if (!bmc150_apply_acpi_orientation(dev, &data->orientation)) {
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
ret = iio_read_mount_matrix(dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1836,7 +1832,6 @@ int bmc150_accel_core_remove(struct device *dev)
|
|||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
|
||||
|
||||
|
|
|
@ -285,11 +285,17 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
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);
|
||||
goto out_read_raw_pm_put;
|
||||
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);
|
||||
if (ret)
|
||||
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;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_ACCEL:
|
||||
pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(data->regmap,
|
||||
BMI088_ACCEL_REG_ACC_RANGE, val);
|
||||
if (ret)
|
||||
|
@ -334,7 +343,10 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
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);
|
||||
goto out_read_raw_pm_put;
|
||||
default:
|
||||
|
@ -376,7 +388,10 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
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);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
@ -496,7 +511,6 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = data->chip_info->channels;
|
||||
indio_dev->num_channels = data->chip_info->num_channels;
|
||||
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_set_suspended(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
bmi088_accel_power_down(data);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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_ */
|
|
@ -28,8 +28,11 @@ struct accel_3d_state {
|
|||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
|
||||
/* Reserve for 3 channels + padding + timestamp */
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3];
|
||||
/* Ensure timestamp is naturally aligned */
|
||||
struct {
|
||||
u32 accel_val[3];
|
||||
s64 timestamp __aligned(8);
|
||||
} scan;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
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);
|
||||
|
||||
hid_sensor_push_data(indio_dev,
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val),
|
||||
&accel_state->scan,
|
||||
sizeof(accel_state->scan),
|
||||
accel_state->timestamp);
|
||||
|
||||
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_Z_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;
|
||||
ret = 0;
|
||||
break;
|
||||
|
|
|
@ -51,13 +51,15 @@
|
|||
#define KXTF9_REG_TILT_POS_CUR 0x10
|
||||
#define KXTF9_REG_TILT_POS_PREV 0x11
|
||||
#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_STATUS_REG 0x18
|
||||
#define KXCJK1013_REG_INT_REL 0x1A
|
||||
#define KXCJK1013_REG_CTRL1 0x1B
|
||||
#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_CTRL2 0x1F
|
||||
#define KXTF9_REG_INT_CTRL3 0x20
|
||||
|
@ -77,6 +79,45 @@
|
|||
#define KXTF9_REG_HYST_SET 0x5F
|
||||
#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_RES BIT(6)
|
||||
#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_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
|
||||
|
||||
enum kx_chipset {
|
||||
|
@ -124,6 +173,7 @@ enum kx_chipset {
|
|||
KXCJ91008,
|
||||
KXTJ21009,
|
||||
KXTF9,
|
||||
KX0231025,
|
||||
KX_MAX_CHIPS /* this must be last */
|
||||
};
|
||||
|
||||
|
@ -133,6 +183,63 @@ enum kx_acpi_type {
|
|||
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 regulator_bulk_data regulators[2];
|
||||
struct i2c_client *client;
|
||||
|
@ -140,7 +247,11 @@ struct kxcjk1013_data {
|
|||
struct iio_trigger *motion_trig;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct mutex mutex;
|
||||
s16 buffer[8];
|
||||
/* Ensure timestamp naturally aligned */
|
||||
struct {
|
||||
s16 chans[AXIS_MAX];
|
||||
s64 timestamp __aligned(8);
|
||||
} scan;
|
||||
u8 odr_bits;
|
||||
u8 range;
|
||||
int wake_thres;
|
||||
|
@ -152,13 +263,7 @@ struct kxcjk1013_data {
|
|||
int64_t timestamp;
|
||||
enum kx_chipset chipset;
|
||||
enum kx_acpi_type acpi_type;
|
||||
};
|
||||
|
||||
enum kxcjk1013_axis {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
AXIS_MAX,
|
||||
const struct kx_chipset_regs *regs;
|
||||
};
|
||||
|
||||
enum kxcjk1013_mode {
|
||||
|
@ -268,6 +373,22 @@ static const struct {
|
|||
{0x05, 5100},
|
||||
{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 {
|
||||
|
@ -309,7 +430,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
|
|||
{
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -320,8 +441,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
|
|||
else
|
||||
ret |= KXCJK1013_REG_CTRL1_BIT_PC1;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
KXCJK1013_REG_CTRL1, ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -335,7 +455,7 @@ static int kxcjk1013_get_mode(struct kxcjk1013_data *data,
|
|||
{
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -353,7 +473,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index)
|
|||
{
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
|
||||
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_1 << 4);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
KXCJK1013_REG_CTRL1,
|
||||
ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -400,7 +518,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
|
|||
if (ret < 0)
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -409,8 +527,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
|
|||
/* Set 12 bit mode */
|
||||
ret |= KXCJK1013_REG_CTRL1_BIT_RES;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL1,
|
||||
ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl\n");
|
||||
return ret;
|
||||
|
@ -421,7 +538,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
|
|||
if (ret < 0)
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_data_ctrl\n");
|
||||
return ret;
|
||||
|
@ -430,7 +547,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
|
|||
data->odr_bits = ret;
|
||||
|
||||
/* 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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -441,13 +558,23 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
|
|||
else
|
||||
ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEA;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1,
|
||||
ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
|
||||
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);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -478,7 +605,7 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
|
|||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = pm_runtime_get_sync(&data->client->dev);
|
||||
ret = pm_runtime_resume_and_get(&data->client->dev);
|
||||
else {
|
||||
pm_runtime_mark_last_busy(&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) {
|
||||
dev_err(&data->client->dev,
|
||||
"Failed: %s for %d\n", __func__, on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&data->client->dev);
|
||||
return ret;
|
||||
}
|
||||
#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)
|
||||
{
|
||||
int waketh_reg, ret;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
KXCJK1013_REG_WAKE_TIMER,
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_timer,
|
||||
data->wake_dur);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
|
@ -508,9 +632,7 @@ static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
waketh_reg = data->chipset == KXTF9 ?
|
||||
KXTF9_REG_WAKE_THRESH : KXCJK1013_REG_WAKE_THRES;
|
||||
ret = i2c_smbus_write_byte_data(data->client, waketh_reg,
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_thres,
|
||||
data->wake_thres);
|
||||
if (ret < 0) {
|
||||
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)
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -550,14 +672,13 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data,
|
|||
else
|
||||
ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1,
|
||||
ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -568,8 +689,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data,
|
|||
else
|
||||
ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
KXCJK1013_REG_CTRL1, ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -599,7 +719,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
|
|||
if (ret < 0)
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -610,14 +730,13 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
|
|||
else
|
||||
ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1,
|
||||
ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -628,8 +747,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
|
|||
else
|
||||
ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
KXCJK1013_REG_CTRL1, ret);
|
||||
ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
|
||||
return ret;
|
||||
|
@ -701,7 +819,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
|
|||
if (ret < 0)
|
||||
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);
|
||||
if (ret < 0) {
|
||||
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;
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
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,
|
||||
KXCJK1013_REG_XOUT_L,
|
||||
AXIS_MAX * 2,
|
||||
(u8 *)data->buffer);
|
||||
(u8 *)data->scan.chans);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
|
||||
data->timestamp);
|
||||
err:
|
||||
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);
|
||||
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)
|
||||
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);
|
||||
|
||||
int ret = i2c_smbus_read_byte_data(data->client,
|
||||
KXCJK1013_REG_INT_SRC2);
|
||||
int ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src2);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_src2\n");
|
||||
return;
|
||||
|
@ -1234,7 +1351,7 @@ static irqreturn_t kxcjk1013_event_handler(int irq, void *private)
|
|||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||
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) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_src1\n");
|
||||
goto ack_intr;
|
||||
|
@ -1257,7 +1374,7 @@ ack_intr:
|
|||
if (data->dready_trigger_on)
|
||||
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)
|
||||
dev_err(&data->client->dev, "Error reading reg_int_rel\n");
|
||||
|
||||
|
@ -1338,8 +1455,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
} else {
|
||||
data->active_high_intr = true; /* default polarity */
|
||||
|
||||
ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
ret = iio_read_mount_matrix(&client->dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1378,6 +1494,22 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
} else
|
||||
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);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -1404,7 +1536,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!data->dready_trig) {
|
||||
ret = -ENOMEM;
|
||||
goto err_poweroff;
|
||||
|
@ -1413,7 +1545,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
data->motion_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
"%s-any-motion-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!data->motion_trig) {
|
||||
ret = -ENOMEM;
|
||||
goto err_poweroff;
|
||||
|
@ -1485,7 +1617,6 @@ static int kxcjk1013_remove(struct i2c_client *client)
|
|||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
if (data->dready_trig) {
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
@ -1593,6 +1724,7 @@ static const struct i2c_device_id kxcjk1013_id[] = {
|
|||
{"kxcj91008", KXCJ91008},
|
||||
{"kxtj21009", KXTJ21009},
|
||||
{"kxtf9", KXTF9},
|
||||
{"kx023-1025", KX0231025},
|
||||
{"SMO8500", KXCJ91008},
|
||||
{}
|
||||
};
|
||||
|
@ -1604,6 +1736,7 @@ static const struct of_device_id kxcjk1013_of_match[] = {
|
|||
{ .compatible = "kionix,kxcj91008", },
|
||||
{ .compatible = "kionix,kxtj21009", },
|
||||
{ .compatible = "kionix,kxtf9", },
|
||||
{ .compatible = "kionix,kx023-1025", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, kxcjk1013_of_match);
|
||||
|
|
|
@ -420,7 +420,7 @@ int kxsd9_common_probe(struct device *dev,
|
|||
indio_dev->available_scan_masks = kxsd9_scan_masks;
|
||||
|
||||
/* 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)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -221,7 +221,7 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
|
|||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(&client->dev);
|
||||
ret = pm_runtime_resume_and_get(&client->dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(&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) {
|
||||
dev_err(&client->dev,
|
||||
"failed to change power state to %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
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",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!trig)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1711,7 +1709,6 @@ static int mma8452_remove(struct i2c_client *client)
|
|||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mma8452_trigger_cleanup(indio_dev);
|
||||
|
|
|
@ -515,7 +515,6 @@ static int mma9551_remove(struct i2c_client *client)
|
|||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
mma9551_set_device_state(data->client, false);
|
||||
|
|
|
@ -664,7 +664,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on)
|
|||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = pm_runtime_get_sync(&client->dev);
|
||||
ret = pm_runtime_resume_and_get(&client->dev);
|
||||
else {
|
||||
pm_runtime_mark_last_busy(&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) {
|
||||
dev_err(&client->dev,
|
||||
"failed to change power state to %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1154,7 +1154,6 @@ static int mma9553_remove(struct i2c_client *client)
|
|||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
mma9551_set_device_state(data->client, false);
|
||||
|
|
|
@ -56,7 +56,11 @@ struct mxc4005_data {
|
|||
struct mutex mutex;
|
||||
struct regmap *regmap;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -135,7 +139,7 @@ static int mxc4005_read_xyz(struct mxc4005_data *data)
|
|||
int ret;
|
||||
|
||||
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) {
|
||||
dev_err(data->dev, "failed to read axes\n");
|
||||
return ret;
|
||||
|
@ -301,7 +305,7 @@ static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
|
|||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
|
||||
pf->timestamp);
|
||||
|
||||
err:
|
||||
|
@ -433,7 +437,7 @@ static int mxc4005_probe(struct i2c_client *client,
|
|||
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!data->dready_trig)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -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, ®_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, ®_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, ®_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, ®_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");
|
|
@ -62,18 +62,6 @@ enum st_accel_type {
|
|||
#define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
|
||||
#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
|
||||
int st_accel_allocate_ring(struct iio_dev *indio_dev);
|
||||
void st_accel_deallocate_ring(struct iio_dev *indio_dev);
|
||||
|
|
|
@ -41,51 +41,74 @@
|
|||
#define ST_ACCEL_FS_AVL_200G 200
|
||||
#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[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1,
|
||||
st_accel_mount_matrix_ext_info),
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1,
|
||||
st_accel_mount_matrix_ext_info),
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
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)
|
||||
};
|
||||
|
||||
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),
|
||||
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR,
|
||||
st_accel_mount_matrix_ext_info),
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR,
|
||||
st_accel_mount_matrix_ext_info),
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
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)
|
||||
};
|
||||
|
||||
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),
|
||||
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR,
|
||||
st_accel_mount_matrix_ext_info),
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR,
|
||||
st_accel_mount_matrix_ext_info),
|
||||
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
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)
|
||||
};
|
||||
|
||||
|
@ -980,7 +1003,99 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.multi_read_bit = true,
|
||||
.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,
|
||||
|
@ -1070,25 +1185,10 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
|
|||
#endif
|
||||
|
||||
#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
|
||||
* appropriate mount matrix.
|
||||
*/
|
||||
static int apply_acpi_orientation(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *channels)
|
||||
static int apply_acpi_orientation(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
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 */
|
||||
adata->mount_matrix = devm_kmalloc(&indio_dev->dev,
|
||||
sizeof(*adata->mount_matrix),
|
||||
GFP_KERNEL);
|
||||
if (!adata->mount_matrix) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (j = 0; j < 3; j++) {
|
||||
int matrix_val = final_ont[i][j];
|
||||
|
@ -1203,26 +1295,25 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev,
|
|||
default:
|
||||
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;
|
||||
dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n");
|
||||
|
||||
out:
|
||||
kfree(buffer.pointer);
|
||||
if (ret)
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"failed to apply ACPI orientation data: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else /* !CONFIG_ACPI */
|
||||
static int apply_acpi_orientation(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *channels)
|
||||
static int apply_acpi_orientation(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
#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_sensors_platform_data *pdata = dev_get_platdata(adata->dev);
|
||||
struct iio_chan_spec *channels;
|
||||
size_t channels_size;
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &accel_info;
|
||||
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_verify_id(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
return err;
|
||||
|
||||
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;
|
||||
|
||||
channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec);
|
||||
channels = devm_kmemdup(&indio_dev->dev,
|
||||
adata->sensor_settings->ch,
|
||||
channels_size, GFP_KERNEL);
|
||||
if (!channels) {
|
||||
err = -ENOMEM;
|
||||
goto st_accel_power_off;
|
||||
/*
|
||||
* First try specific ACPI methods to retrieve orientation then try the
|
||||
* generic function.
|
||||
*/
|
||||
err = apply_acpi_orientation(indio_dev);
|
||||
if (err) {
|
||||
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->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);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
return err;
|
||||
|
||||
err = st_accel_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
return err;
|
||||
|
||||
if (adata->irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
|
@ -1315,9 +1398,6 @@ st_accel_device_register_error:
|
|||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
st_accel_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
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);
|
||||
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (adata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
|
|
@ -174,16 +174,29 @@ static int st_accel_i2c_probe(struct i2c_client *client)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = st_accel_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
ret = st_sensors_power_enable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = st_accel_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
return 0;
|
||||
|
||||
st_accel_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -123,16 +123,29 @@ static int st_accel_spi_probe(struct spi_device *spi)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
return 0;
|
||||
|
||||
st_accel_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
* IIO driver for STK8312; 7-bit I2C address: 0x3D.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -103,7 +102,11 @@ struct stk8312_data {
|
|||
u8 mode;
|
||||
struct iio_trigger *dready_trig;
|
||||
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);
|
||||
|
@ -438,7 +441,7 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p)
|
|||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
STK8312_REG_XOUT,
|
||||
STK8312_ALL_CHANNEL_SIZE,
|
||||
data->buffer);
|
||||
data->scan.chans);
|
||||
if (ret < STK8312_ALL_CHANNEL_SIZE) {
|
||||
dev_err(&data->client->dev, "register read failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
|
@ -452,12 +455,12 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p)
|
|||
mutex_unlock(&data->lock);
|
||||
goto err;
|
||||
}
|
||||
data->buffer[i++] = ret;
|
||||
data->scan.chans[i++] = ret;
|
||||
}
|
||||
}
|
||||
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);
|
||||
err:
|
||||
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,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!data->dready_trig) {
|
||||
ret = -ENOMEM;
|
||||
goto err_power_off;
|
||||
|
@ -635,23 +638,17 @@ static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
|
|||
#endif
|
||||
|
||||
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);
|
||||
|
||||
static const struct acpi_device_id stk8312_acpi_id[] = {
|
||||
{"STK8312", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
|
||||
|
||||
static struct i2c_driver stk8312_driver = {
|
||||
.driver = {
|
||||
.name = STK8312_DRIVER_NAME,
|
||||
.pm = STK8312_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
|
||||
},
|
||||
.probe = stk8312_probe,
|
||||
.remove = stk8312_remove,
|
||||
|
|
|
@ -91,12 +91,11 @@ struct stk8ba50_data {
|
|||
u8 sample_rate_idx;
|
||||
struct iio_trigger *dready_trig;
|
||||
bool dready_trigger_on;
|
||||
/*
|
||||
* 3 x 16-bit channels (10-bit data, 6-bit padding) +
|
||||
* 1 x 16 padding +
|
||||
* 4 x 16 64-bit timestamp
|
||||
*/
|
||||
s16 buffer[8];
|
||||
/* Ensure timestamp is naturally aligned */
|
||||
struct {
|
||||
s16 chans[3];
|
||||
s64 timetamp __aligned(8);
|
||||
} scan;
|
||||
};
|
||||
|
||||
#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,
|
||||
STK8BA50_REG_XOUT,
|
||||
STK8BA50_ALL_CHANNEL_SIZE,
|
||||
(u8 *)data->buffer);
|
||||
(u8 *)data->scan.chans);
|
||||
if (ret < STK8BA50_ALL_CHANNEL_SIZE) {
|
||||
dev_err(&data->client->dev, "register read failed\n");
|
||||
goto err;
|
||||
|
@ -337,10 +336,10 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p)
|
|||
if (ret < 0)
|
||||
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);
|
||||
err:
|
||||
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,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (!data->dready_trig) {
|
||||
ret = -ENOMEM;
|
||||
goto err_power_off;
|
||||
|
|
|
@ -1190,6 +1190,18 @@ config TI_TLC4541
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
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
|
||||
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
||||
depends on TWL4030_CORE
|
||||
|
|
|
@ -106,6 +106,7 @@ obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
|
|||
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
|
||||
obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.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);
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{"ad7298", 0},
|
||||
{}
|
||||
|
@ -355,6 +362,7 @@ MODULE_DEVICE_TABLE(spi, ad7298_id);
|
|||
static struct spi_driver ad7298_driver = {
|
||||
.driver = {
|
||||
.name = "ad7298",
|
||||
.acpi_match_table = ad7298_acpi_ids,
|
||||
},
|
||||
.probe = ad7298_probe,
|
||||
.id_table = ad7298_id,
|
||||
|
|
|
@ -32,12 +32,14 @@ struct ad7476_chip_info {
|
|||
/* channels used when convst gpio is defined */
|
||||
struct iio_chan_spec convst_channel[2];
|
||||
void (*reset)(struct ad7476_state *);
|
||||
bool has_vref;
|
||||
bool has_vdrive;
|
||||
};
|
||||
|
||||
struct ad7476_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad7476_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct regulator *ref_reg;
|
||||
struct gpio_desc *convst_gpio;
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
|
@ -52,13 +54,17 @@ struct ad7476_state {
|
|||
};
|
||||
|
||||
enum ad7476_supported_device_ids {
|
||||
ID_AD7091,
|
||||
ID_AD7091R,
|
||||
ID_AD7273,
|
||||
ID_AD7274,
|
||||
ID_AD7276,
|
||||
ID_AD7277,
|
||||
ID_AD7278,
|
||||
ID_AD7466,
|
||||
ID_AD7467,
|
||||
ID_AD7468,
|
||||
ID_AD7475,
|
||||
ID_AD7495,
|
||||
ID_AD7940,
|
||||
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);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (!st->chip_info->int_vref_uv) {
|
||||
scale_uv = regulator_get_voltage(st->reg);
|
||||
if (st->ref_reg) {
|
||||
scale_uv = regulator_get_voltage(st->ref_reg);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
} else {
|
||||
|
@ -187,13 +193,32 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|||
BIT(IIO_CHAN_INFO_RAW))
|
||||
|
||||
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
[ID_AD7091R] = {
|
||||
[ID_AD7091] = {
|
||||
.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),
|
||||
.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] = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.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[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] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.int_vref_uv = 2500000,
|
||||
.has_vdrive = true,
|
||||
},
|
||||
[ID_AD7940] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
|
@ -254,6 +286,7 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
|||
[ID_LTC2314_14] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.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)
|
||||
{
|
||||
struct ad7476_state *st = data;
|
||||
struct regulator *reg = data;
|
||||
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int ad7476_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7476_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *reg;
|
||||
int ret;
|
||||
|
||||
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 =
|
||||
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable,
|
||||
st);
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg);
|
||||
if (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,
|
||||
"adi,conversion-start",
|
||||
GPIOD_OUT_LOW);
|
||||
|
@ -333,17 +421,17 @@ static int ad7476_probe(struct spi_device *spi)
|
|||
}
|
||||
|
||||
static const struct spi_device_id ad7476_id[] = {
|
||||
{"ad7091", ID_AD7091R},
|
||||
{"ad7091", ID_AD7091},
|
||||
{"ad7091r", ID_AD7091R},
|
||||
{"ad7273", ID_AD7277},
|
||||
{"ad7274", ID_AD7276},
|
||||
{"ad7273", ID_AD7273},
|
||||
{"ad7274", ID_AD7274},
|
||||
{"ad7276", ID_AD7276},
|
||||
{"ad7277", ID_AD7277},
|
||||
{"ad7278", ID_AD7278},
|
||||
{"ad7466", ID_AD7466},
|
||||
{"ad7467", ID_AD7467},
|
||||
{"ad7468", ID_AD7468},
|
||||
{"ad7475", ID_AD7466},
|
||||
{"ad7475", ID_AD7475},
|
||||
{"ad7476", ID_AD7466},
|
||||
{"ad7476a", ID_AD7466},
|
||||
{"ad7477", ID_AD7467},
|
||||
|
|
|
@ -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",
|
||||
indio_dev->name, indio_dev->id);
|
||||
indio_dev->name,
|
||||
iio_device_id(indio_dev));
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -248,7 +248,8 @@ static int ad7766_probe(struct spi_device *spi)
|
|||
|
||||
if (spi->irq > 0) {
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -289,10 +290,7 @@ static int ad7766_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7766_id[] = {
|
||||
|
|
|
@ -630,7 +630,8 @@ static int ad7768_probe(struct spi_device *spi)
|
|||
}
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -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,
|
||||
"%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (sigma_delta->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
|
|
|
@ -202,29 +202,25 @@ static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv)
|
|||
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,
|
||||
size_t sizeof_priv)
|
||||
{
|
||||
struct adi_axi_adc_conv **ptr, *conv;
|
||||
|
||||
ptr = devres_alloc(devm_adi_axi_adc_conv_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
struct adi_axi_adc_conv *conv;
|
||||
int ret;
|
||||
|
||||
conv = adi_axi_adc_conv_register(dev, sizeof_priv);
|
||||
if (IS_ERR(conv)) {
|
||||
devres_free(ptr);
|
||||
return ERR_CAST(conv);
|
||||
}
|
||||
if (IS_ERR(conv))
|
||||
return conv;
|
||||
|
||||
*ptr = conv;
|
||||
devres_add(dev, ptr);
|
||||
ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release,
|
||||
conv);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
|
|
@ -997,7 +997,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
|
|||
int ret;
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -547,7 +547,7 @@ static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
|
|||
char *name = kasprintf(GFP_KERNEL,
|
||||
"%s-dev%d-%s",
|
||||
idev->name,
|
||||
idev->id,
|
||||
iio_device_id(idev),
|
||||
triggers[i].name);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
@ -626,7 +626,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
|
|||
int ret;
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -649,7 +649,8 @@ static int dln2_adc_probe(struct platform_device *pdev)
|
|||
indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
|
||||
|
||||
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) {
|
||||
dev_err(dev, "failed to allocate trigger\n");
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -165,10 +165,8 @@ static int ep93xx_adc_probe(struct platform_device *pdev)
|
|||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->base)) {
|
||||
dev_err(&pdev->dev, "Cannot map memory resource\n");
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
}
|
||||
|
||||
iiodev->name = dev_name(&pdev->dev);
|
||||
iiodev->modes = INDIO_DIRECT_MODE;
|
||||
|
|
|
@ -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 iio_dev *indio_dev = NULL;
|
||||
bool has_ts = false;
|
||||
int ret = -ENODEV;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
|
||||
|
|
|
@ -843,7 +843,8 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
|
|||
chip->allow_async_readout);
|
||||
|
||||
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);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
@ -63,7 +64,7 @@ static int max11100_read_single(struct iio_dev *indio_dev, int *val)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = (state->buffer[1] << 8) | state->buffer[2];
|
||||
*val = get_unaligned_be16(&state->buffer[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -101,6 +102,11 @@ static const struct iio_info max11100_info = {
|
|||
.read_raw = max11100_read_raw,
|
||||
};
|
||||
|
||||
static void max11100_regulator_disable(void *reg)
|
||||
{
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int max11100_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
@ -111,8 +117,6 @@ static int max11100_probe(struct spi_device *spi)
|
|||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->spi = spi;
|
||||
|
||||
|
@ -130,27 +134,12 @@ static int max11100_probe(struct spi_device *spi)
|
|||
if (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)
|
||||
goto disable_regulator;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
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;
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id max11100_ids[] = {
|
||||
|
@ -165,7 +154,6 @@ static struct spi_driver max11100_driver = {
|
|||
.of_match_table = max11100_ids,
|
||||
},
|
||||
.probe = max11100_probe,
|
||||
.remove = max11100_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(max11100_driver);
|
||||
|
|
|
@ -66,9 +66,8 @@ static const struct iio_chan_spec max1118_channels[] = {
|
|||
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 spi_transfer xfers[] = {
|
||||
/*
|
||||
|
@ -103,9 +102,9 @@ static int max1118_read(struct spi_device *spi, int channel)
|
|||
int ret;
|
||||
|
||||
if (channel == 0)
|
||||
ret = spi_sync_transfer(spi, xfers + 1, 2);
|
||||
ret = spi_sync_transfer(adc->spi, xfers + 1, 2);
|
||||
else
|
||||
ret = spi_sync_transfer(spi, xfers, 3);
|
||||
ret = spi_sync_transfer(adc->spi, xfers, 3);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -113,11 +112,10 @@ static int max1118_read(struct spi_device *spi, int channel)
|
|||
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);
|
||||
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;
|
||||
|
||||
switch (id->driver_data) {
|
||||
|
@ -144,14 +142,14 @@ static int max1118_read_raw(struct iio_dev *indio_dev,
|
|||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
*val = max1118_read(adc->spi, chan->channel);
|
||||
*val = max1118_read(indio_dev, chan->channel);
|
||||
mutex_unlock(&adc->lock);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = max1118_get_vref_mV(adc->spi);
|
||||
*val = max1118_get_vref_mV(indio_dev);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
*val2 = 8;
|
||||
|
@ -180,7 +178,7 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p)
|
|||
indio_dev->masklength) {
|
||||
const struct iio_chan_spec *scan_chan =
|
||||
&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) {
|
||||
dev_warn(&adc->spi->dev,
|
||||
|
@ -201,6 +199,11 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void max1118_reg_disable(void *reg)
|
||||
{
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int max1118_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
|
@ -225,9 +228,13 @@ static int max1118_probe(struct spi_device *spi)
|
|||
ret = regulator_enable(adc->reg);
|
||||
if (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->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
|
||||
* 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,
|
||||
max1118_trigger_handler, NULL);
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
|
||||
max1118_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto err_reg_disable;
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(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;
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
.probe = max1118_probe,
|
||||
.remove = max1118_remove,
|
||||
.id_table = max1118_id,
|
||||
};
|
||||
module_spi_driver(max1118_spi_driver);
|
||||
|
|
|
@ -144,7 +144,6 @@ static int mp2629_adc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
indio_dev->name = "mp2629-adc";
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = mp2629_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mp2629_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
|
|
@ -337,7 +337,6 @@ static int mt6360_adc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &mt6360_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mt6360_adc_channels;
|
||||
|
|
|
@ -455,7 +455,7 @@ static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
|
|||
struct mxs_lradc_adc *adc = iio_priv(iio);
|
||||
|
||||
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
|
||||
iio->id);
|
||||
iio_device_id(iio));
|
||||
if (!trig)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
return pm_runtime_resume_and_get(dev);
|
||||
} else {
|
||||
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,
|
||||
|
@ -535,7 +530,10 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
|
|||
pm_runtime_use_autosuspend(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_start(priv);
|
||||
|
||||
|
@ -552,6 +550,7 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
|
|||
err_iio_device_register:
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
pm_runtime_put_sync(dev);
|
||||
err_power_up:
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
|
|
@ -549,6 +549,7 @@ static const struct of_device_id sc27xx_adc_of_match[] = {
|
|||
{ .compatible = "sprd,sc2731-adc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sc27xx_adc_of_match);
|
||||
|
||||
static struct platform_driver sc27xx_adc_driver = {
|
||||
.probe = sc27xx_adc_probe,
|
||||
|
|
|
@ -449,7 +449,7 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
|
|||
.smp_bits = stm32h7_smp_bits,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* STM32 ADC registers access routines
|
||||
* @adc: stm32 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;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Fixed timeout value for ADC calibration.
|
||||
* worst cases:
|
||||
* - low clock frequency
|
||||
|
@ -1158,11 +1158,9 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
|
|||
|
||||
adc->bufi = 0;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Apply sampling time settings */
|
||||
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;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!readval)
|
||||
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;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
|
||||
if (ret) {
|
||||
|
|
|
@ -135,11 +135,9 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
|
|||
int ret;
|
||||
|
||||
if (atomic_inc_return(&priv->n_active_ch) == 1) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
|
||||
clk_src = priv->aclk ? 1 : 0;
|
||||
|
|
|
@ -146,6 +146,11 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void adc081c_reg_disable(void *reg)
|
||||
{
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int adc081c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -175,6 +180,11 @@ static int adc081c_probe(struct i2c_client *client,
|
|||
if (err < 0)
|
||||
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->modes = INDIO_DIRECT_MODE;
|
||||
iio->info = &adc081c_info;
|
||||
|
@ -182,38 +192,14 @@ static int adc081c_probe(struct i2c_client *client,
|
|||
iio->channels = model->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) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
goto err_regulator_disable;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = iio_device_register(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;
|
||||
return devm_iio_device_register(&client->dev, iio);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adc081c_id[] = {
|
||||
|
@ -238,7 +224,6 @@ static struct i2c_driver adc081c_driver = {
|
|||
.of_match_table = adc081c_of_match,
|
||||
},
|
||||
.probe = adc081c_probe,
|
||||
.remove = adc081c_remove,
|
||||
.id_table = adc081c_id,
|
||||
};
|
||||
module_i2c_driver(adc081c_driver);
|
||||
|
|
|
@ -236,6 +236,11 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void adc0832_reg_disable(void *reg)
|
||||
{
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int adc0832_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
|
@ -287,36 +292,17 @@ static int adc0832_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
adc0832_trigger_handler, NULL);
|
||||
ret = devm_add_action_or_reset(&spi->dev, adc0832_reg_disable,
|
||||
adc->reg);
|
||||
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)
|
||||
goto err_buffer_cleanup;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
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;
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
.probe = adc0832_probe,
|
||||
.remove = adc0832_remove,
|
||||
.id_table = adc0832_id,
|
||||
};
|
||||
module_spi_driver(adc0832_driver);
|
||||
|
|
|
@ -215,6 +215,11 @@ static const struct iio_info adc108s102_info = {
|
|||
.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)
|
||||
{
|
||||
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");
|
||||
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);
|
||||
if (ret < 0) {
|
||||
|
@ -249,7 +258,6 @@ static int adc108s102_probe(struct spi_device *spi)
|
|||
st->va_millivolt = ret / 1000;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
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,
|
||||
&st->scan_single_xfer, 1);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&adc108s102_trigger_handler, NULL);
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
|
||||
&adc108s102_trigger_handler,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
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;
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{ .compatible = "ti,adc108s102" },
|
||||
{ }
|
||||
|
@ -327,7 +313,6 @@ static struct spi_driver adc108s102_driver = {
|
|||
.acpi_match_table = ACPI_PTR(adc108s102_acpi_ids),
|
||||
},
|
||||
.probe = adc108s102_probe,
|
||||
.remove = adc108s102_remove,
|
||||
.id_table = adc108s102_id,
|
||||
};
|
||||
module_spi_driver(adc108s102_driver);
|
||||
|
|
|
@ -169,6 +169,11 @@ static const struct iio_info ti_adc_info = {
|
|||
.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)
|
||||
{
|
||||
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->name = TI_ADC_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->spi = spi;
|
||||
|
@ -203,42 +207,24 @@ static int ti_adc_probe(struct spi_device *spi)
|
|||
}
|
||||
|
||||
data->ref = devm_regulator_get(&spi->dev, "vdda");
|
||||
if (!IS_ERR(data->ref)) {
|
||||
ret = regulator_enable(data->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(data->ref))
|
||||
return PTR_ERR(data->ref);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ti_adc_trigger_handler, NULL);
|
||||
ret = regulator_enable(data->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable,
|
||||
data->ref);
|
||||
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)
|
||||
goto error_unreg_buffer;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
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;
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
.probe = ti_adc_probe,
|
||||
.remove = ti_adc_remove,
|
||||
.id_table = ti_adc_id,
|
||||
};
|
||||
module_spi_driver(ti_adc_driver);
|
||||
|
|
|
@ -395,10 +395,14 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->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;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(&scan, 0, sizeof(scan));
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
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;
|
||||
}
|
||||
|
||||
buf[0] = res;
|
||||
scan.chan = res;
|
||||
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));
|
||||
|
||||
err:
|
||||
|
|
|
@ -830,7 +830,6 @@ static int ads131e08_probe(struct spi_device *spi)
|
|||
return ret;
|
||||
|
||||
indio_dev->name = st->info->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &ads131e08_iio_info;
|
||||
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",
|
||||
indio_dev->name, indio_dev->id);
|
||||
indio_dev->name, iio_device_id(indio_dev));
|
||||
if (!st->trig) {
|
||||
dev_err(&spi->dev, "failed to allocate IIO trigger\n");
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -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", ®);
|
||||
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");
|
|
@ -167,7 +167,11 @@ struct vf610_adc {
|
|||
u32 sample_freq_avail[5];
|
||||
|
||||
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 };
|
||||
|
@ -579,9 +583,9 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
|||
if (coco & VF610_ADC_HS_COCO0) {
|
||||
info->value = vf610_adc_read_data(info);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
info->buffer[0] = info->value;
|
||||
info->scan.chan = info->value;
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
info->buffer,
|
||||
&info->scan,
|
||||
iio_get_time_ns(indio_dev));
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
} else
|
||||
|
|
|
@ -743,7 +743,7 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
|
|||
int ret;
|
||||
|
||||
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)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ struct rescale {
|
|||
struct iio_channel *source;
|
||||
struct iio_chan_spec chan;
|
||||
struct iio_chan_spec_ext_info *ext_info;
|
||||
bool chan_processed;
|
||||
s32 numerator;
|
||||
s32 denominator;
|
||||
};
|
||||
|
@ -43,10 +44,27 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
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:
|
||||
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) {
|
||||
case IIO_VAL_FRACTIONAL:
|
||||
*val *= rescale->numerator;
|
||||
|
@ -130,16 +148,27 @@ static int rescale_configure_channel(struct device *dev,
|
|||
chan->ext_info = rescale->ext_info;
|
||||
chan->type = rescale->cfg->type;
|
||||
|
||||
if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) ||
|
||||
!iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) {
|
||||
dev_err(dev, "source channel does not support raw/scale\n");
|
||||
if (iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) ||
|
||||
iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) {
|
||||
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;
|
||||
}
|
||||
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -228,9 +228,9 @@ static void iio_dmaengine_buffer_free(struct iio_buffer *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,
|
||||
const char *channel)
|
||||
{
|
||||
struct iio_buffer **bufferp, *buffer;
|
||||
|
||||
bufferp = devres_alloc(__devm_iio_dmaengine_buffer_free,
|
||||
sizeof(*bufferp), GFP_KERNEL);
|
||||
if (!bufferp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = iio_dmaengine_buffer_alloc(dev, channel);
|
||||
if (IS_ERR(buffer)) {
|
||||
devres_free(bufferp);
|
||||
if (IS_ERR(buffer))
|
||||
return buffer;
|
||||
}
|
||||
|
||||
*bufferp = buffer;
|
||||
devres_add(dev, bufferp);
|
||||
ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
|
||||
buffer);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -137,9 +137,9 @@ void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
|
|||
}
|
||||
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 **ptr, *iio_hwc;
|
||||
|
||||
ptr = devres_alloc(devm_iio_hw_consumer_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
struct iio_hw_consumer *iio_hwc;
|
||||
int ret;
|
||||
|
||||
iio_hwc = iio_hw_consumer_alloc(dev);
|
||||
if (IS_ERR(iio_hwc)) {
|
||||
devres_free(ptr);
|
||||
} else {
|
||||
*ptr = iio_hwc;
|
||||
devres_add(dev, ptr);
|
||||
}
|
||||
if (IS_ERR(iio_hwc))
|
||||
return iio_hwc;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, devm_iio_hw_consumer_release,
|
||||
iio_hwc);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return iio_hwc;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
|
|||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
iio_device_id(indio_dev));
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_kfifo_free;
|
||||
|
@ -96,9 +96,9 @@ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
|
|||
}
|
||||
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,
|
||||
|
@ -108,24 +108,15 @@ int devm_iio_triggered_buffer_setup_ext(struct device *dev,
|
|||
const struct iio_buffer_setup_ops *ops,
|
||||
const struct attribute **buffer_attrs)
|
||||
{
|
||||
struct iio_dev **ptr;
|
||||
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,
|
||||
buffer_attrs);
|
||||
if (!ret)
|
||||
devres_add(dev, ptr);
|
||||
else
|
||||
devres_free(ptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -132,17 +132,32 @@ config SENSIRION_SGP30
|
|||
module will be called sgp30.
|
||||
|
||||
config SPS30
|
||||
tristate "SPS30 particulate matter sensor"
|
||||
depends on I2C
|
||||
select CRC8
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config SPS30_I2C
|
||||
tristate "SPS30 particulate matter sensor I2C driver"
|
||||
depends on I2C
|
||||
select SPS30
|
||||
select CRC8
|
||||
help
|
||||
Say Y here to build support for the Sensirion SPS30 particulate
|
||||
matter sensor.
|
||||
Say Y here to build support for the Sensirion SPS30 I2C interface
|
||||
driver.
|
||||
|
||||
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
|
||||
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
|
||||
|
|
|
@ -17,4 +17,6 @@ obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
|
|||
obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o
|
||||
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue