diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-06-09 12:11:49 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-06-09 12:11:49 +0200 |
commit | 6771fb0b940eb74f1a68fe3f180a7668103397d3 (patch) | |
tree | 0d17a52002350df09f684277831dd939f63883d3 | |
parent | 54fd727f83a4d2f9c6e85cb1fad88325a56b555f (diff) | |
parent | 41340965b4f8055f975f73e1e3d23eff8038f013 (diff) | |
download | linux-6771fb0b940eb74f1a68fe3f180a7668103397d3.tar.gz linux-6771fb0b940eb74f1a68fe3f180a7668103397d3.tar.bz2 linux-6771fb0b940eb74f1a68fe3f180a7668103397d3.zip |
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
...
212 files changed, 8761 insertions, 1803 deletions
diff --git a/Documentation/ABI/obsolete/sysfs-bus-iio b/Documentation/ABI/obsolete/sysfs-bus-iio new file mode 100644 index 000000000000..c9531bb64816 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-bus-iio @@ -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 diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index 566bd99fe0a5..e9d9e50f03be 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -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 diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 267973541e72..29c1097c5722 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -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 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 index 544548ee794c..7fe6935d1448 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 @@ -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. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index 66b621f10223..a8e04b41d9ff 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -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. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-icm42600 b/Documentation/ABI/testing/sysfs-bus-iio-icm42600 deleted file mode 100644 index 0bf1fd4f5bf1..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-icm42600 +++ /dev/null @@ -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). diff --git a/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als b/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als index 22c5ea670971..c476d48d0f82 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als +++ b/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als @@ -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. diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml new file mode 100644 index 000000000000..6f8f8a6258fe --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml new file mode 100644 index 000000000000..942b23ad0712 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml b/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml new file mode 100644 index 000000000000..7c8f8bdc2333 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml @@ -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"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml index fbb714431e3d..52fa0f7c2d0e 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml @@ -16,6 +16,7 @@ properties: - kionix,kxcj91008 - kionix,kxtj21009 - kionix,kxtf9 + - kionix,kx023-1025 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml b/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml new file mode 100644 index 000000000000..55fd3548e3b6 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml new file mode 100644 index 000000000000..ad529ab2c6e2 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml @@ -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"; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index 912a7635edc4..db348fcbb52c 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -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 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml new file mode 100644 index 000000000000..ca414bb396c5 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml new file mode 100644 index 000000000000..cf711082ad7d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml new file mode 100644 index 000000000000..601d69971d84 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml @@ -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>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/afe/current-sense-shunt.yaml b/Documentation/devicetree/bindings/iio/afe/current-sense-shunt.yaml index 90439a8dc785..f8a112c9a822 100644 --- a/Documentation/devicetree/bindings/iio/afe/current-sense-shunt.yaml +++ b/Documentation/devicetree/bindings/iio/afe/current-sense-shunt.yaml @@ -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>; diff --git a/Documentation/devicetree/bindings/iio/cdc/adi,ad7746.yaml b/Documentation/devicetree/bindings/iio/cdc/adi,ad7746.yaml new file mode 100644 index 000000000000..a02036ef9e8d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/cdc/adi,ad7746.yaml @@ -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; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml index a93d1972a5c2..967500b7e773 100644 --- a/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml @@ -22,7 +22,6 @@ properties: required: - compatible - - reg additionalProperties: false @@ -37,5 +36,11 @@ examples: reg = <0x69>; }; }; + - | + serial { + air-pollution-sensor { + compatible = "sensirion,sps30"; + }; + }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/ad5755.txt b/Documentation/devicetree/bindings/iio/dac/ad5755.txt deleted file mode 100644 index 502e1e55adbd..000000000000 --- a/Documentation/devicetree/bindings/iio/dac/ad5755.txt +++ /dev/null @@ -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>; - }; -}; diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml new file mode 100644 index 000000000000..be419ac46caa --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml @@ -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>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml new file mode 100644 index 000000000000..b0157050f1ee --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/ti-dac082s085.txt b/Documentation/devicetree/bindings/iio/dac/ti-dac082s085.txt deleted file mode 100644 index 9cb0e10df704..000000000000 --- a/Documentation/devicetree/bindings/iio/dac/ti-dac082s085.txt +++ /dev/null @@ -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>; - }; diff --git a/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml b/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml new file mode 100644 index 000000000000..83b88c6a243d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index 7e98f47987dc..b2a1e42c56fa 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -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>; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml new file mode 100644 index 000000000000..347bc16a4671 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -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>; + }; + }; diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 8341e9d23c1e..35e58fe46999 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index 503fd21901f1..9c55fdcc1514 100644 --- a/MAINTAINERS +++ b/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) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 5328705aa09c..d5d2540b30c2 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -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 diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index cb646ed2f039..19742e6f5e3e 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -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 diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c new file mode 100644 index 000000000000..ab10ba33f46a --- /dev/null +++ b/drivers/counter/intel-qep.c @@ -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"); diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 8b1723635cce..17f6bdcf1db3 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -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 diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 32cd1342a31a..89280e823bcd 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -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 diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index fe225990de24..7a434e2884d4 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -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> diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index 6c2d4a967de7..ac08e866d612 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -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> diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 9c9a896a872a..fc9592407717 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -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; diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index b8a7469cdae4..4a07e60c0e21 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -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; diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 36fc9876dbca..0622c7936499 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -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); diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 7eeba80e32cb..21520e022a21 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -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; diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 04d85ce34e9f..46ab7675186c 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -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); diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index 12d00658e46f..a06dae5c971d 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -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; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c new file mode 100644 index 000000000000..078d87865fde --- /dev/null +++ b/drivers/iio/accel/fxls8962af-core.c @@ -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"); diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c new file mode 100644 index 000000000000..cfb004b20455 --- /dev/null +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -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"); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c new file mode 100644 index 000000000000..57108d3d480b --- /dev/null +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -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"); diff --git a/drivers/iio/accel/fxls8962af.h b/drivers/iio/accel/fxls8962af.h new file mode 100644 index 000000000000..b67572c3ef06 --- /dev/null +++ b/drivers/iio/accel/fxls8962af.h @@ -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_ */ diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 2f9465cb382f..27f47e1c251e 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -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; diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index ff724bc17a45..a51fdd3c9b5b 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -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); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 0e18b92e2099..bf7ed9e7d00f 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -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; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 4d307dfb9169..715b8138fb71 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -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); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 08a2303cc9df..4c359fb05480 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -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); diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index 666e7a04a7d7..fbf2e2c45678 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -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; } diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index c15908faa381..ba3ecb3b57dc 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -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); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index fb3cbaa62bd8..b3afbf064915 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -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; diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c new file mode 100644 index 000000000000..f7ef8ecfd34a --- /dev/null +++ b/drivers/iio/accel/sca3300.c @@ -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"); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 5d356288e001..f5b0b8bbaff7 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -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); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 43c50167d220..28fceac9f2f6 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -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); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 360e16f2cadb..95e305b88d5e 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -174,16 +174,29 @@ static int st_accel_i2c_probe(struct i2c_client *client) if (ret < 0) return ret; + ret = st_sensors_power_enable(indio_dev); + if (ret) + return ret; + ret = st_accel_common_probe(indio_dev); if (ret < 0) - return ret; + 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; } diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 568ff1bae0ee..83d3308ce5cc 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -123,16 +123,29 @@ static int st_accel_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_accel_common_probe(indio_dev); if (err < 0) - return err; + 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; } diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 157d8faefb9e..43c621d0f11e 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -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, diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 7cf9cb7e8666..e137a34b5c9a 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -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; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index c7946c439612..db0c8fb60515 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -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 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a226657d19c0..f70d877c555a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -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 diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index d2163cb62f4f..3f4e73f7d35a 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -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, diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 9e9ff07cf972..9d5a71df02b0 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -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_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}, diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 0af0bb4d5a7f..0a60ecc69d38 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -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; diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 1e41759f3ee5..9b35c09b9313 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -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[] = { diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 60f21fed6dcb..4db3b480a871 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -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; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index e777ec718973..69b979331ccd 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -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; diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index d5f6ffc5b5bc..a73e3c2d212f 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -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; } diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index a7826f097b95..6e8c28675947 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -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; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 0b5f0c91d0d7..5a7d3a3a5fa8 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -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; diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 0d53ef18e045..16407664182c 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -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; diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index c08ab3c6dfaf..a10a4e8d94fd 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -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; diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 8c98d8c9ab1f..3b3868aa2533 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -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)); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 2ae54258b221..a4b2ff9e0dd5 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -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); diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c index 6cf21758ca66..eb1ce6a0315c 100644 --- a/drivers/iio/adc/max11100.c +++ b/drivers/iio/adc/max11100.c @@ -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 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 ret; - 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); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 6efb0b43d938..8cec9d949083 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -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); - - ret = iio_triggered_buffer_setup(indio_dev, NULL, - max1118_trigger_handler, NULL); - if (ret) - goto err_reg_disable; + max1118_read(indio_dev, 0); - ret = iio_device_register(indio_dev); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + max1118_trigger_handler, NULL); 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 ret; - 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); diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 331a9a728217..aca084f1e78a 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -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; diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index 6b39a139ce28..07c0e6768391 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -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; diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index 30e29f44ebd2..1d99170d3328 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -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; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 9f38cf3c7dc2..a48895046408 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -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); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index 301cf66de695..00098caf6d9e 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -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, diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index b25386b19373..5088de835bb1 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -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) { diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index bb925a11c8ae..a627af9a825e 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -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; diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index b64718daa201..16fc608db36a 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -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); diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index 0261b3cfc92b..fb5e72600b96 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -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 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 ret; - 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); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index 183b2245e89b..db902aef2abe 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -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); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 607791ffe7f0..75ca7f1c8726 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -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); - if (ret) - goto error_regulator_disable; + ret = regulator_enable(data->ref); + if (ret < 0) + return ret; - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable, + data->ref); if (ret) - goto error_unreg_buffer; - - return 0; + return ret; -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); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ti_adc_trigger_handler, NULL); + if (ret) + return ret; - 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); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 9fef39bcf997..5b828428be77 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -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: diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 764dab087b41..0c2025a22575 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -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; diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c new file mode 100644 index 000000000000..cf5373d5cdd7 --- /dev/null +++ b/drivers/iio/adc/ti-tsc2046.c @@ -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"); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 1d794cf3e3f1..fd57fc43e8e5 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -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 diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 6914c1900ed0..198d2916266d 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -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); diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index e42ea2b1707d..774eb3044edd 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -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; diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index d76179878ff9..1ac94c4e9792 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -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; } diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index f2d27788f666..87d9aabd20c7 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -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; } diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index b2b1b7d27af4..f77c4538141e 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -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); diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 10bb431bc3ce..a4920646e9be 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -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_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. + be called sps30_serial. config VZ89X tristate "SGX Sensortech MiCS VZ89X VOC sensor" diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index fef63dd5bf92..4898690cc155 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -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 diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 56ba6c82b501..0fdb3b29c5eb 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -91,8 +91,8 @@ struct atlas_data { struct regmap *regmap; struct irq_work work; unsigned int interrupt_enabled; - - __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ + /* 96-bit data + 32-bit pad + 64-bit timestamp */ + __be32 buffer[6] __aligned(8); }; static const struct regmap_config atlas_regmap_config = { @@ -640,7 +640,7 @@ static int atlas_probe(struct i2c_client *client, indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, iio_device_id(indio_dev)); if (!trig) return -ENOMEM; diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 29c0dfa4702b..74cf89c82c0a 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -11,7 +11,6 @@ * Note: SDO pin cannot be left floating otherwise I2C address * will be undefined. */ -#include <linux/acpi.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> @@ -42,12 +41,6 @@ static const struct i2c_device_id bme680_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, bme680_i2c_id); -static const struct acpi_device_id bme680_acpi_match[] = { - {"BME0680", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, bme680_acpi_match); - static const struct of_device_id bme680_of_i2c_match[] = { { .compatible = "bosch,bme680", }, {}, @@ -57,7 +50,6 @@ MODULE_DEVICE_TABLE(of, bme680_of_i2c_match); static struct i2c_driver bme680_i2c_driver = { .driver = { .name = "bme680_i2c", - .acpi_match_table = ACPI_PTR(bme680_acpi_match), .of_match_table = bme680_of_i2c_match, }, .probe = bme680_i2c_probe, diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 6f56ad48cc40..cc579a7ac5ce 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -4,7 +4,6 @@ * * Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com> */ -#include <linux/acpi.h> #include <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> @@ -145,12 +144,6 @@ static const struct spi_device_id bme680_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, bme680_spi_id); -static const struct acpi_device_id bme680_acpi_match[] = { - {"BME0680", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, bme680_acpi_match); - static const struct of_device_id bme680_of_spi_match[] = { { .compatible = "bosch,bme680", }, {}, @@ -160,7 +153,6 @@ MODULE_DEVICE_TABLE(of, bme680_of_spi_match); static struct spi_driver bme680_spi_driver = { .driver = { .name = "bme680_spi", - .acpi_match_table = ACPI_PTR(bme680_acpi_match), .of_match_table = bme680_of_spi_match, }, .probe = bme680_spi_probe, diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 886e96496dbf..847194fa1e46 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -491,7 +491,7 @@ static int ccs811_probe(struct i2c_client *client, data->drdy_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->drdy_trig) { ret = -ENOMEM; goto err_poweroff; diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index d89f117dd0ef..9fe6bbe9ee04 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -640,7 +640,8 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) struct iio_trigger *trig; int ret; - trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); if (!trig) { dev_err(dev, "failed to allocate trigger\n"); return -ENOMEM; diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index 1029c457be15..2343d444604d 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -425,7 +425,7 @@ static int sgp_check_compat(struct sgp_data *data, product = SGP_VERS_PRODUCT(data); if (product != product_id) { - dev_err(dev, "sensor reports a different product: 0x%04hx\n", + dev_err(dev, "sensor reports a different product: 0x%04x\n", product); return -ENODEV; } diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index 2ea9a5c4d846..d51314505115 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -3,11 +3,8 @@ * Sensirion SPS30 particulate matter sensor driver * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> - * - * I2C slave address: 0x69 */ -#include <asm/unaligned.h> #include <linux/crc8.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -19,27 +16,14 @@ #include <linux/kernel.h> #include <linux/module.h> -#define SPS30_CRC8_POLYNOMIAL 0x31 -/* max number of bytes needed to store PM measurements or serial string */ -#define SPS30_MAX_READ_SIZE 48 +#include "sps30.h" + /* sensor measures reliably up to 3000 ug / m3 */ #define SPS30_MAX_PM 3000 /* minimum and maximum self cleaning periods in seconds */ #define SPS30_AUTO_CLEANING_PERIOD_MIN 0 #define SPS30_AUTO_CLEANING_PERIOD_MAX 604800 -/* SPS30 commands */ -#define SPS30_START_MEAS 0x0010 -#define SPS30_STOP_MEAS 0x0104 -#define SPS30_RESET 0xd304 -#define SPS30_READ_DATA_READY_FLAG 0x0202 -#define SPS30_READ_DATA 0x0300 -#define SPS30_READ_SERIAL 0xd033 -#define SPS30_START_FAN_CLEANING 0x5607 -#define SPS30_AUTO_CLEANING_PERIOD 0x8004 -/* not a sensor command per se, used only to distinguish write from read */ -#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005 - enum { PM1, PM2P5, @@ -52,114 +36,9 @@ enum { MEASURING, }; -struct sps30_state { - struct i2c_client *client; - /* - * Guards against concurrent access to sensor registers. - * Must be held whenever sequence of commands is to be executed. - */ - struct mutex lock; - int state; -}; - -DECLARE_CRC8_TABLE(sps30_crc8_table); - -static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf, - int txsize, u8 *rxbuf, int rxsize) -{ - int ret; - - /* - * Sensor does not support repeated start so instead of - * sending two i2c messages in a row we just send one by one. - */ - ret = i2c_master_send(state->client, txbuf, txsize); - if (ret != txsize) - return ret < 0 ? ret : -EIO; - - if (!rxbuf) - return 0; - - ret = i2c_master_recv(state->client, rxbuf, rxsize); - if (ret != rxsize) - return ret < 0 ? ret : -EIO; - - return 0; -} - -static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size) -{ - /* - * Internally sensor stores measurements in a following manner: - * - * PM1: upper two bytes, crc8, lower two bytes, crc8 - * PM2P5: upper two bytes, crc8, lower two bytes, crc8 - * PM4: upper two bytes, crc8, lower two bytes, crc8 - * PM10: upper two bytes, crc8, lower two bytes, crc8 - * - * What follows next are number concentration measurements and - * typical particle size measurement which we omit. - */ - u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd }; - int i, ret = 0; - - switch (cmd) { - case SPS30_START_MEAS: - buf[2] = 0x03; - buf[3] = 0x00; - buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); - ret = sps30_write_then_read(state, buf, 5, NULL, 0); - break; - case SPS30_STOP_MEAS: - case SPS30_RESET: - case SPS30_START_FAN_CLEANING: - ret = sps30_write_then_read(state, buf, 2, NULL, 0); - break; - case SPS30_READ_AUTO_CLEANING_PERIOD: - buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8; - buf[1] = (u8)(SPS30_AUTO_CLEANING_PERIOD & 0xff); - fallthrough; - case SPS30_READ_DATA_READY_FLAG: - case SPS30_READ_DATA: - case SPS30_READ_SERIAL: - /* every two data bytes are checksummed */ - size += size / 2; - ret = sps30_write_then_read(state, buf, 2, buf, size); - break; - case SPS30_AUTO_CLEANING_PERIOD: - buf[2] = data[0]; - buf[3] = data[1]; - buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); - buf[5] = data[2]; - buf[6] = data[3]; - buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE); - ret = sps30_write_then_read(state, buf, 8, NULL, 0); - break; - } - - if (ret) - return ret; - - /* validate received data and strip off crc bytes */ - for (i = 0; i < size; i += 3) { - u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE); - - if (crc != buf[i + 2]) { - dev_err(&state->client->dev, - "data integrity check failed\n"); - return -EIO; - } - - *data++ = buf[i]; - *data++ = buf[i + 1]; - } - - return 0; -} - -static s32 sps30_float_to_int_clamped(const u8 *fp) +static s32 sps30_float_to_int_clamped(__be32 *fp) { - int val = get_unaligned_be32(fp); + int val = be32_to_cpup(fp); int mantissa = val & GENMASK(22, 0); /* this is fine since passed float is always non-negative */ int exp = val >> 23; @@ -188,38 +67,35 @@ static s32 sps30_float_to_int_clamped(const u8 *fp) static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) { - int i, ret, tries = 5; - u8 tmp[16]; + int i, ret; if (state->state == RESET) { - ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0); + ret = state->ops->start_meas(state); if (ret) return ret; state->state = MEASURING; } - while (tries--) { - ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2); - if (ret) - return -EIO; + ret = state->ops->read_meas(state, (__be32 *)data, size); + if (ret) + return ret; - /* new measurements ready to be read */ - if (tmp[1] == 1) - break; + for (i = 0; i < size; i++) + data[i] = sps30_float_to_int_clamped((__be32 *)&data[i]); - msleep_interruptible(300); - } + return 0; +} - if (tries == -1) - return -ETIMEDOUT; +static int sps30_do_reset(struct sps30_state *state) +{ + int ret; - ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size); + ret = state->ops->reset(state); if (ret) return ret; - for (i = 0; i < size; i++) - data[i] = sps30_float_to_int_clamped(&tmp[4 * i]); + state->state = RESET; return 0; } @@ -310,24 +186,6 @@ static int sps30_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int sps30_do_cmd_reset(struct sps30_state *state) -{ - int ret; - - ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0); - msleep(300); - /* - * Power-on-reset causes sensor to produce some glitch on i2c bus and - * some controllers end up in error state. Recover simply by placing - * some data on the bus, for example STOP_MEAS command, which - * is NOP in this case. - */ - sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); - state->state = RESET; - - return ret; -} - static ssize_t start_cleaning_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -340,7 +198,7 @@ static ssize_t start_cleaning_store(struct device *dev, return -EINVAL; mutex_lock(&state->lock); - ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0); + ret = state->ops->clean_fan(state); mutex_unlock(&state->lock); if (ret) return ret; @@ -349,31 +207,29 @@ static ssize_t start_cleaning_store(struct device *dev, } static ssize_t cleaning_period_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct sps30_state *state = iio_priv(indio_dev); - u8 tmp[4]; + __be32 val; int ret; mutex_lock(&state->lock); - ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4); + ret = state->ops->read_cleaning_period(state, &val); mutex_unlock(&state->lock); if (ret) return ret; - return sprintf(buf, "%d\n", get_unaligned_be32(tmp)); + return sprintf(buf, "%d\n", be32_to_cpu(val)); } -static ssize_t cleaning_period_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static ssize_t cleaning_period_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct sps30_state *state = iio_priv(indio_dev); int val, ret; - u8 tmp[4]; if (kstrtoint(buf, 0, &val)) return -EINVAL; @@ -382,10 +238,8 @@ static ssize_t cleaning_period_store(struct device *dev, (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) return -EINVAL; - put_unaligned_be32(val, tmp); - mutex_lock(&state->lock); - ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0); + ret = state->ops->write_cleaning_period(state, cpu_to_be32(val)); if (ret) { mutex_unlock(&state->lock); return ret; @@ -397,7 +251,7 @@ static ssize_t cleaning_period_store(struct device *dev, * sensor requires reset in order to return up to date self cleaning * period */ - ret = sps30_do_cmd_reset(state); + ret = sps30_do_reset(state); if (ret) dev_warn(dev, "period changed but reads will return the old value\n"); @@ -411,9 +265,9 @@ static ssize_t cleaning_period_available_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n", - SPS30_AUTO_CLEANING_PERIOD_MIN, 1, - SPS30_AUTO_CLEANING_PERIOD_MAX); + return sysfs_emit(buf, "[%d %d %d]\n", + SPS30_AUTO_CLEANING_PERIOD_MIN, 1, + SPS30_AUTO_CLEANING_PERIOD_MAX); } static IIO_DEVICE_ATTR_WO(start_cleaning, 0); @@ -460,90 +314,65 @@ static const struct iio_chan_spec sps30_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; -static void sps30_stop_meas(void *data) +static void sps30_devm_stop_meas(void *data) { struct sps30_state *state = data; - sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); + if (state->state == MEASURING) + state->ops->stop_meas(state); } static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 }; -static int sps30_probe(struct i2c_client *client) +int sps30_probe(struct device *dev, const char *name, void *priv, const struct sps30_ops *ops) { struct iio_dev *indio_dev; struct sps30_state *state; - u8 buf[32]; int ret; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -EOPNOTSUPP; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); if (!indio_dev) return -ENOMEM; + dev_set_drvdata(dev, indio_dev); + state = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - state->client = client; - state->state = RESET; + state->dev = dev; + state->priv = priv; + state->ops = ops; + mutex_init(&state->lock); + indio_dev->info = &sps30_info; - indio_dev->name = client->name; + indio_dev->name = name; indio_dev->channels = sps30_channels; indio_dev->num_channels = ARRAY_SIZE(sps30_channels); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->available_scan_masks = sps30_scan_masks; - mutex_init(&state->lock); - crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL); - - ret = sps30_do_cmd_reset(state); + ret = sps30_do_reset(state); if (ret) { - dev_err(&client->dev, "failed to reset device\n"); + dev_err(dev, "failed to reset device\n"); return ret; } - ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf)); + ret = state->ops->show_info(state); if (ret) { - dev_err(&client->dev, "failed to read serial number\n"); + dev_err(dev, "failed to read device info\n"); return ret; } - /* returned serial number is already NUL terminated */ - dev_info(&client->dev, "serial number: %s\n", buf); - ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state); + ret = devm_add_action_or_reset(dev, sps30_devm_stop_meas, state); if (ret) return ret; - ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, sps30_trigger_handler, NULL); if (ret) return ret; - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } - -static const struct i2c_device_id sps30_id[] = { - { "sps30" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, sps30_id); - -static const struct of_device_id sps30_of_match[] = { - { .compatible = "sensirion,sps30" }, - { } -}; -MODULE_DEVICE_TABLE(of, sps30_of_match); - -static struct i2c_driver sps30_driver = { - .driver = { - .name = "sps30", - .of_match_table = sps30_of_match, - }, - .id_table = sps30_id, - .probe_new = sps30_probe, -}; -module_i2c_driver(sps30_driver); +EXPORT_SYMBOL_GPL(sps30_probe); MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver"); diff --git a/drivers/iio/chemical/sps30.h b/drivers/iio/chemical/sps30.h new file mode 100644 index 000000000000..a58ee43cf45d --- /dev/null +++ b/drivers/iio/chemical/sps30.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SPS30_H +#define _SPS30_H + +#include <linux/types.h> + +struct sps30_state; +struct sps30_ops { + int (*start_meas)(struct sps30_state *state); + int (*stop_meas)(struct sps30_state *state); + int (*read_meas)(struct sps30_state *state, __be32 *meas, size_t num); + int (*reset)(struct sps30_state *state); + int (*clean_fan)(struct sps30_state *state); + int (*read_cleaning_period)(struct sps30_state *state, __be32 *period); + int (*write_cleaning_period)(struct sps30_state *state, __be32 period); + int (*show_info)(struct sps30_state *state); +}; + +struct sps30_state { + /* serialize access to the device */ + struct mutex lock; + struct device *dev; + int state; + /* + * priv pointer is solely for serdev driver private data. We keep it + * here because driver_data inside dev has been already used for iio and + * struct serdev_device doesn't have one. + */ + void *priv; + const struct sps30_ops *ops; +}; + +int sps30_probe(struct device *dev, const char *name, void *priv, const struct sps30_ops *ops); + +#endif diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c new file mode 100644 index 000000000000..d33560ed7184 --- /dev/null +++ b/drivers/iio/chemical/sps30_i2c.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SPS30 particulate matter sensor i2c driver + * + * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * + * I2C slave address: 0x69 + */ +#include <asm/unaligned.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/types.h> + +#include "sps30.h" + +#define SPS30_I2C_CRC8_POLYNOMIAL 0x31 +/* max number of bytes needed to store PM measurements or serial string */ +#define SPS30_I2C_MAX_BUF_SIZE 48 + +DECLARE_CRC8_TABLE(sps30_i2c_crc8_table); + +#define SPS30_I2C_START_MEAS 0x0010 +#define SPS30_I2C_STOP_MEAS 0x0104 +#define SPS30_I2C_READ_MEAS 0x0300 +#define SPS30_I2C_MEAS_READY 0x0202 +#define SPS30_I2C_RESET 0xd304 +#define SPS30_I2C_CLEAN_FAN 0x5607 +#define SPS30_I2C_PERIOD 0x8004 +#define SPS30_I2C_READ_SERIAL 0xd033 +#define SPS30_I2C_READ_VERSION 0xd100 + +static int sps30_i2c_xfer(struct sps30_state *state, unsigned char *txbuf, size_t txsize, + unsigned char *rxbuf, size_t rxsize) +{ + struct i2c_client *client = to_i2c_client(state->dev); + int ret; + + /* + * Sensor does not support repeated start so instead of + * sending two i2c messages in a row we just send one by one. + */ + ret = i2c_master_send(client, txbuf, txsize); + if (ret < 0) + return ret; + if (ret != txsize) + return -EIO; + + if (!rxsize) + return 0; + + ret = i2c_master_recv(client, rxbuf, rxsize); + if (ret < 0) + return ret; + if (ret != rxsize) + return -EIO; + + return 0; +} + +static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size_t arg_size, + void *rsp, size_t rsp_size) +{ + /* + * Internally sensor stores measurements in a following manner: + * + * PM1: upper two bytes, crc8, lower two bytes, crc8 + * PM2P5: upper two bytes, crc8, lower two bytes, crc8 + * PM4: upper two bytes, crc8, lower two bytes, crc8 + * PM10: upper two bytes, crc8, lower two bytes, crc8 + * + * What follows next are number concentration measurements and + * typical particle size measurement which we omit. + */ + unsigned char buf[SPS30_I2C_MAX_BUF_SIZE]; + unsigned char *tmp; + unsigned char crc; + size_t i; + int ret; + + put_unaligned_be16(cmd, buf); + i = 2; + + if (rsp) { + /* each two bytes are followed by a crc8 */ + rsp_size += rsp_size / 2; + } else { + tmp = arg; + + while (arg_size) { + buf[i] = *tmp++; + buf[i + 1] = *tmp++; + buf[i + 2] = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + arg_size -= 2; + i += 3; + } + } + + ret = sps30_i2c_xfer(state, buf, i, buf, rsp_size); + if (ret) + return ret; + + /* validate received data and strip off crc bytes */ + tmp = rsp; + for (i = 0; i < rsp_size; i += 3) { + crc = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + if (crc != buf[i + 2]) { + dev_err(state->dev, "data integrity check failed\n"); + return -EIO; + } + + *tmp++ = buf[i]; + *tmp++ = buf[i + 1]; + } + + return 0; +} + +static int sps30_i2c_start_meas(struct sps30_state *state) +{ + /* request BE IEEE754 formatted data */ + unsigned char buf[] = { 0x03, 0x00 }; + + return sps30_i2c_command(state, SPS30_I2C_START_MEAS, buf, sizeof(buf), NULL, 0); +} + +static int sps30_i2c_stop_meas(struct sps30_state *state) +{ + return sps30_i2c_command(state, SPS30_I2C_STOP_MEAS, NULL, 0, NULL, 0); +} + +static int sps30_i2c_reset(struct sps30_state *state) +{ + int ret; + + ret = sps30_i2c_command(state, SPS30_I2C_RESET, NULL, 0, NULL, 0); + msleep(500); + /* + * Power-on-reset causes sensor to produce some glitch on i2c bus and + * some controllers end up in error state. Recover simply by placing + * some data on the bus, for example STOP_MEAS command, which + * is NOP in this case. + */ + sps30_i2c_stop_meas(state); + + return ret; +} + +static bool sps30_i2c_meas_ready(struct sps30_state *state) +{ + unsigned char buf[2]; + int ret; + + ret = sps30_i2c_command(state, SPS30_I2C_MEAS_READY, NULL, 0, buf, sizeof(buf)); + if (ret) + return false; + + return buf[1]; +} + +static int sps30_i2c_read_meas(struct sps30_state *state, __be32 *meas, size_t num) +{ + /* measurements are ready within a second */ + if (msleep_interruptible(1000)) + return -EINTR; + + if (!sps30_i2c_meas_ready(state)) + return -ETIMEDOUT; + + return sps30_i2c_command(state, SPS30_I2C_READ_MEAS, NULL, 0, meas, sizeof(num) * num); +} + +static int sps30_i2c_clean_fan(struct sps30_state *state) +{ + return sps30_i2c_command(state, SPS30_I2C_CLEAN_FAN, NULL, 0, NULL, 0); +} + +static int sps30_i2c_read_cleaning_period(struct sps30_state *state, __be32 *period) +{ + return sps30_i2c_command(state, SPS30_I2C_PERIOD, NULL, 0, period, sizeof(*period)); +} + +static int sps30_i2c_write_cleaning_period(struct sps30_state *state, __be32 period) +{ + return sps30_i2c_command(state, SPS30_I2C_PERIOD, &period, sizeof(period), NULL, 0); +} + +static int sps30_i2c_show_info(struct sps30_state *state) +{ + /* extra nul just in case */ + unsigned char buf[32 + 1] = { 0x00 }; + int ret; + + ret = sps30_i2c_command(state, SPS30_I2C_READ_SERIAL, NULL, 0, buf, sizeof(buf) - 1); + if (ret) + return ret; + + dev_info(state->dev, "serial number: %s\n", buf); + + ret = sps30_i2c_command(state, SPS30_I2C_READ_VERSION, NULL, 0, buf, 2); + if (ret) + return ret; + + dev_info(state->dev, "fw version: %u.%u\n", buf[0], buf[1]); + + return 0; +} + +static const struct sps30_ops sps30_i2c_ops = { + .start_meas = sps30_i2c_start_meas, + .stop_meas = sps30_i2c_stop_meas, + .read_meas = sps30_i2c_read_meas, + .reset = sps30_i2c_reset, + .clean_fan = sps30_i2c_clean_fan, + .read_cleaning_period = sps30_i2c_read_cleaning_period, + .write_cleaning_period = sps30_i2c_write_cleaning_period, + .show_info = sps30_i2c_show_info, +}; + +static int sps30_i2c_probe(struct i2c_client *client) +{ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + crc8_populate_msb(sps30_i2c_crc8_table, SPS30_I2C_CRC8_POLYNOMIAL); + + return sps30_probe(&client->dev, client->name, NULL, &sps30_i2c_ops); +} + +static const struct i2c_device_id sps30_i2c_id[] = { + { "sps30" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sps30_i2c_id); + +static const struct of_device_id sps30_i2c_of_match[] = { + { .compatible = "sensirion,sps30" }, + { } +}; +MODULE_DEVICE_TABLE(of, sps30_i2c_of_match); + +static struct i2c_driver sps30_i2c_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = sps30_i2c_of_match, + }, + .id_table = sps30_i2c_id, + .probe_new = sps30_i2c_probe, +}; +module_i2c_driver(sps30_i2c_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c new file mode 100644 index 000000000000..3f311d50087c --- /dev/null +++ b/drivers/iio/chemical/sps30_serial.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SPS30 particulate matter sensor serial driver + * + * Copyright (c) 2021 Tomasz Duszynski <tomasz.duszynski@octakon.com> + */ +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/iio.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/serdev.h> +#include <linux/types.h> + +#include "sps30.h" + +#define SPS30_SERIAL_DEV_NAME "sps30" + +#define SPS30_SERIAL_SOF_EOF 0x7e +#define SPS30_SERIAL_TIMEOUT msecs_to_jiffies(20) +#define SPS30_SERIAL_MAX_BUF_SIZE 263 +#define SPS30_SERIAL_ESCAPE_CHAR 0x7d + +#define SPS30_SERIAL_FRAME_MIN_SIZE 7 +#define SPS30_SERIAL_FRAME_ADR_OFFSET 1 +#define SPS30_SERIAL_FRAME_CMD_OFFSET 2 +#define SPS30_SERIAL_FRAME_MOSI_LEN_OFFSET 3 +#define SPS30_SERIAL_FRAME_MISO_STATE_OFFSET 3 +#define SPS30_SERIAL_FRAME_MISO_LEN_OFFSET 4 +#define SPS30_SERIAL_FRAME_MISO_DATA_OFFSET 5 + +#define SPS30_SERIAL_START_MEAS 0x00 +#define SPS30_SERIAL_STOP_MEAS 0x01 +#define SPS30_SERIAL_READ_MEAS 0x03 +#define SPS30_SERIAL_RESET 0xd3 +#define SPS30_SERIAL_CLEAN_FAN 0x56 +#define SPS30_SERIAL_PERIOD 0x80 +#define SPS30_SERIAL_DEV_INFO 0xd0 +#define SPS30_SERIAL_READ_VERSION 0xd1 + +struct sps30_serial_priv { + struct completion new_frame; + unsigned char buf[SPS30_SERIAL_MAX_BUF_SIZE]; + size_t num; + bool escaped; + bool done; +}; + +static int sps30_serial_xfer(struct sps30_state *state, const unsigned char *buf, size_t size) +{ + struct serdev_device *serdev = to_serdev_device(state->dev); + struct sps30_serial_priv *priv = state->priv; + int ret; + + priv->num = 0; + priv->escaped = false; + priv->done = false; + + ret = serdev_device_write(serdev, buf, size, SPS30_SERIAL_TIMEOUT); + if (ret < 0) + return ret; + if (ret != size) + return -EIO; + + ret = wait_for_completion_interruptible_timeout(&priv->new_frame, SPS30_SERIAL_TIMEOUT); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +static const struct { + unsigned char byte; + unsigned char byte2; +} sps30_serial_bytes[] = { + { 0x11, 0x31 }, + { 0x13, 0x33 }, + { 0x7e, 0x5e }, + { 0x7d, 0x5d }, +}; + +static int sps30_serial_put_byte(unsigned char *buf, unsigned char byte) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sps30_serial_bytes); i++) { + if (sps30_serial_bytes[i].byte != byte) + continue; + + buf[0] = SPS30_SERIAL_ESCAPE_CHAR; + buf[1] = sps30_serial_bytes[i].byte2; + + return 2; + } + + buf[0] = byte; + + return 1; +} + +static char sps30_serial_get_byte(bool escaped, unsigned char byte2) +{ + int i; + + if (!escaped) + return byte2; + + for (i = 0; i < ARRAY_SIZE(sps30_serial_bytes); i++) { + if (sps30_serial_bytes[i].byte2 != byte2) + continue; + + return sps30_serial_bytes[i].byte; + } + + return 0; +} + +static unsigned char sps30_serial_calc_chksum(const unsigned char *buf, size_t num) +{ + unsigned int chksum = 0; + size_t i; + + for (i = 0; i < num; i++) + chksum += buf[i]; + + return ~chksum; +} + +static int sps30_serial_prep_frame(unsigned char *buf, unsigned char cmd, + const unsigned char *arg, size_t arg_size) +{ + unsigned char chksum; + int num = 0; + size_t i; + + buf[num++] = SPS30_SERIAL_SOF_EOF; + buf[num++] = 0; + num += sps30_serial_put_byte(buf + num, cmd); + num += sps30_serial_put_byte(buf + num, arg_size); + + for (i = 0; i < arg_size; i++) + num += sps30_serial_put_byte(buf + num, arg[i]); + + /* SOF isn't checksummed */ + chksum = sps30_serial_calc_chksum(buf + 1, num - 1); + num += sps30_serial_put_byte(buf + num, chksum); + buf[num++] = SPS30_SERIAL_SOF_EOF; + + return num; +} + +static bool sps30_serial_frame_valid(struct sps30_state *state, const unsigned char *buf) +{ + struct sps30_serial_priv *priv = state->priv; + unsigned char chksum; + + if ((priv->num < SPS30_SERIAL_FRAME_MIN_SIZE) || + (priv->num != SPS30_SERIAL_FRAME_MIN_SIZE + + priv->buf[SPS30_SERIAL_FRAME_MISO_LEN_OFFSET])) { + dev_err(state->dev, "frame has invalid number of bytes\n"); + return false; + } + + if ((priv->buf[SPS30_SERIAL_FRAME_ADR_OFFSET] != buf[SPS30_SERIAL_FRAME_ADR_OFFSET]) || + (priv->buf[SPS30_SERIAL_FRAME_CMD_OFFSET] != buf[SPS30_SERIAL_FRAME_CMD_OFFSET])) { + dev_err(state->dev, "frame has wrong ADR and CMD bytes\n"); + return false; + } + + if (priv->buf[SPS30_SERIAL_FRAME_MISO_STATE_OFFSET]) { + dev_err(state->dev, "frame with non-zero state received (0x%02x)\n", + priv->buf[SPS30_SERIAL_FRAME_MISO_STATE_OFFSET]); + return false; + } + + /* SOF, checksum and EOF are not checksummed */ + chksum = sps30_serial_calc_chksum(priv->buf + 1, priv->num - 3); + if (priv->buf[priv->num - 2] != chksum) { + dev_err(state->dev, "frame integrity check failed\n"); + return false; + } + + return true; +} + +static int sps30_serial_command(struct sps30_state *state, unsigned char cmd, + const void *arg, size_t arg_size, void *rsp, size_t rsp_size) +{ + struct sps30_serial_priv *priv = state->priv; + unsigned char buf[SPS30_SERIAL_MAX_BUF_SIZE]; + int ret, size; + + size = sps30_serial_prep_frame(buf, cmd, arg, arg_size); + ret = sps30_serial_xfer(state, buf, size); + if (ret) + return ret; + + if (!sps30_serial_frame_valid(state, buf)) + return -EIO; + + if (rsp) { + rsp_size = min_t(size_t, priv->buf[SPS30_SERIAL_FRAME_MISO_LEN_OFFSET], rsp_size); + memcpy(rsp, &priv->buf[SPS30_SERIAL_FRAME_MISO_DATA_OFFSET], rsp_size); + } + + return rsp_size; +} + +static int sps30_serial_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); + struct sps30_serial_priv *priv; + struct sps30_state *state; + unsigned char byte; + size_t i; + + if (!indio_dev) + return 0; + + state = iio_priv(indio_dev); + priv = state->priv; + + /* just in case device put some unexpected data on the bus */ + if (priv->done) + return size; + + /* wait for the start of frame */ + if (!priv->num && size && buf[0] != SPS30_SERIAL_SOF_EOF) + return 1; + + if (priv->num + size >= ARRAY_SIZE(priv->buf)) + size = ARRAY_SIZE(priv->buf) - priv->num; + + for (i = 0; i < size; i++) { + byte = buf[i]; + /* remove stuffed bytes on-the-fly */ + if (byte == SPS30_SERIAL_ESCAPE_CHAR) { + priv->escaped = true; + continue; + } + + byte = sps30_serial_get_byte(priv->escaped, byte); + if (priv->escaped && !byte) + dev_warn(state->dev, "unrecognized escaped char (0x%02x)\n", byte); + + priv->buf[priv->num++] = byte; + + /* EOF received */ + if (!priv->escaped && byte == SPS30_SERIAL_SOF_EOF) { + if (priv->num < SPS30_SERIAL_FRAME_MIN_SIZE) + continue; + + priv->done = true; + complete(&priv->new_frame); + i++; + break; + } + + priv->escaped = false; + } + + return i; +} + +static const struct serdev_device_ops sps30_serial_device_ops = { + .receive_buf = sps30_serial_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int sps30_serial_start_meas(struct sps30_state *state) +{ + /* request BE IEEE754 formatted data */ + unsigned char buf[] = { 0x01, 0x03 }; + + return sps30_serial_command(state, SPS30_SERIAL_START_MEAS, buf, sizeof(buf), NULL, 0); +} + +static int sps30_serial_stop_meas(struct sps30_state *state) +{ + return sps30_serial_command(state, SPS30_SERIAL_STOP_MEAS, NULL, 0, NULL, 0); +} + +static int sps30_serial_reset(struct sps30_state *state) +{ + int ret; + + ret = sps30_serial_command(state, SPS30_SERIAL_RESET, NULL, 0, NULL, 0); + msleep(500); + + return ret; +} + +static int sps30_serial_read_meas(struct sps30_state *state, __be32 *meas, size_t num) +{ + int ret; + + /* measurements are ready within a second */ + if (msleep_interruptible(1000)) + return -EINTR; + + ret = sps30_serial_command(state, SPS30_SERIAL_READ_MEAS, NULL, 0, meas, num * sizeof(num)); + if (ret < 0) + return ret; + /* if measurements aren't ready sensor returns empty frame */ + if (ret == SPS30_SERIAL_FRAME_MIN_SIZE) + return -ETIMEDOUT; + if (ret != num * sizeof(*meas)) + return -EIO; + + return 0; +} + +static int sps30_serial_clean_fan(struct sps30_state *state) +{ + return sps30_serial_command(state, SPS30_SERIAL_CLEAN_FAN, NULL, 0, NULL, 0); +} + +static int sps30_serial_read_cleaning_period(struct sps30_state *state, __be32 *period) +{ + unsigned char buf[] = { 0x00 }; + int ret; + + ret = sps30_serial_command(state, SPS30_SERIAL_PERIOD, buf, sizeof(buf), + period, sizeof(*period)); + if (ret < 0) + return ret; + if (ret != sizeof(*period)) + return -EIO; + + return 0; +} + +static int sps30_serial_write_cleaning_period(struct sps30_state *state, __be32 period) +{ + unsigned char buf[5] = { 0x00 }; + + memcpy(buf + 1, &period, sizeof(period)); + + return sps30_serial_command(state, SPS30_SERIAL_PERIOD, buf, sizeof(buf), NULL, 0); +} + +static int sps30_serial_show_info(struct sps30_state *state) +{ + /* + * tell device do return serial number and add extra nul byte just in case + * serial number isn't a valid string + */ + unsigned char buf[32 + 1] = { 0x03 }; + struct device *dev = state->dev; + int ret; + + ret = sps30_serial_command(state, SPS30_SERIAL_DEV_INFO, buf, 1, buf, sizeof(buf) - 1); + if (ret < 0) + return ret; + if (ret != sizeof(buf) - 1) + return -EIO; + + dev_info(dev, "serial number: %s\n", buf); + + ret = sps30_serial_command(state, SPS30_SERIAL_READ_VERSION, NULL, 0, buf, sizeof(buf) - 1); + if (ret < 0) + return ret; + if (ret < 2) + return -EIO; + + dev_info(dev, "fw version: %u.%u\n", buf[0], buf[1]); + + return 0; +} + +static const struct sps30_ops sps30_serial_ops = { + .start_meas = sps30_serial_start_meas, + .stop_meas = sps30_serial_stop_meas, + .read_meas = sps30_serial_read_meas, + .reset = sps30_serial_reset, + .clean_fan = sps30_serial_clean_fan, + .read_cleaning_period = sps30_serial_read_cleaning_period, + .write_cleaning_period = sps30_serial_write_cleaning_period, + .show_info = sps30_serial_show_info, +}; + +static int sps30_serial_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct sps30_serial_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + init_completion(&priv->new_frame); + serdev_device_set_client_ops(serdev, &sps30_serial_device_ops); + + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, 115200); + serdev_device_set_flow_control(serdev, false); + + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) + return ret; + + return sps30_probe(dev, SPS30_SERIAL_DEV_NAME, priv, &sps30_serial_ops); +} + +static const struct of_device_id sps30_serial_of_match[] = { + { .compatible = "sensirion,sps30" }, + { } +}; +MODULE_DEVICE_TABLE(of, sps30_serial_of_match); + +static struct serdev_device_driver sps30_serial_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = sps30_serial_of_match, + }, + .probe = sps30_serial_probe, +}; +module_serdev_device_driver(sps30_serial_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 95ddccb44f1c..c06537e106e9 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -163,18 +163,15 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) if (state) { atomic_inc(&st->user_requested_state); - ret = pm_runtime_get_sync(&st->pdev->dev); + ret = pm_runtime_resume_and_get(&st->pdev->dev); } else { atomic_dec(&st->user_requested_state); pm_runtime_mark_last_busy(&st->pdev->dev); pm_runtime_use_autosuspend(&st->pdev->dev); ret = pm_runtime_put_autosuspend(&st->pdev->dev); } - if (ret < 0) { - if (state) - pm_runtime_put_noidle(&st->pdev->dev); + if (ret < 0) return ret; - } return 0; #else @@ -222,7 +219,6 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, pm_runtime_disable(&attrb->pdev->dev); pm_runtime_set_suspended(&attrb->pdev->dev); - pm_runtime_put_noidle(&attrb->pdev->dev); cancel_work_sync(&attrb->work); iio_trigger_unregister(attrb->trigger); @@ -256,7 +252,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, } trig = iio_trigger_alloc(indio_dev->dev.parent, - "%s-dev%d", name, indio_dev->id); + "%s-dev%d", name, iio_device_id(indio_dev)); if (trig == NULL) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); ret = -ENOMEM; diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 141e8aa6911e..7cf2bf282cef 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -525,7 +525,6 @@ scmi_alloc_iiodev(struct scmi_device *sdev, return ERR_PTR(-ENOMEM); iiodev->modes = INDIO_DIRECT_MODE; - iiodev->dev.parent = dev; sensor = iio_priv(iiodev); sensor->sensor_ops = ops; sensor->ph = ph; diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index 79837a4b3a41..3104ec32dfac 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -597,8 +597,6 @@ static int ad5766_probe(struct spi_device *spi) indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; indio_dev->info = &ad5766_info; - indio_dev->dev.parent = &spi->dev; - indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index a5b0a52bf86e..dd2e306824e7 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -69,9 +69,8 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, } if (enable) { - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { - pm_runtime_put_noidle(dev); mutex_unlock(&dac->lock); return ret; } diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index a11ae9db0d11..36879f01e28c 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -6,19 +6,14 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> #include <linux/sysfs.h> #include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #include <linux/debugfs.h> @@ -223,13 +218,12 @@ static ssize_t adis16136_read_frequency(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct adis16136 *adis16136 = iio_priv(indio_dev); - struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; int ret; - mutex_lock(slock); + adis_dev_lock(&adis16136->adis); ret = __adis16136_get_freq(adis16136, &freq); - mutex_unlock(slock); + adis_dev_unlock(&adis16136->adis); if (ret) return ret; @@ -254,11 +248,10 @@ static const unsigned adis16136_3db_divisors[] = { static int adis16136_set_filter(struct iio_dev *indio_dev, int val) { struct adis16136 *adis16136 = iio_priv(indio_dev); - struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; int i, ret; - mutex_lock(slock); + adis_dev_lock(&adis16136->adis); ret = __adis16136_get_freq(adis16136, &freq); if (ret) goto out_unlock; @@ -270,7 +263,7 @@ static int adis16136_set_filter(struct iio_dev *indio_dev, int val) ret = __adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); out_unlock: - mutex_unlock(slock); + adis_dev_unlock(&adis16136->adis); return ret; } @@ -278,12 +271,11 @@ out_unlock: static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) { struct adis16136 *adis16136 = iio_priv(indio_dev); - struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; uint16_t val16; int ret; - mutex_lock(slock); + adis_dev_lock(&adis16136->adis); ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16); @@ -297,7 +289,7 @@ static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) *val = freq / adis16136_3db_divisors[val16 & 0x07]; err_unlock: - mutex_unlock(slock); + adis_dev_unlock(&adis16136->adis); return ret ? ret : IIO_VAL_INT; } diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index e7c9a3e31c45..66b6b7bd5e1b 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -5,17 +5,12 @@ * Copyright 2010 Analog Devices Inc. */ -#include <linux/interrupt.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/sysfs.h> #include <linux/module.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #define ADIS16260_STARTUP_DELAY 220 /* ms */ @@ -293,7 +288,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); case IIO_CHAN_INFO_SAMP_FREQ: - mutex_lock(&adis->state_lock); + adis_dev_lock(adis); if (spi_get_device_id(adis->spi)->driver_data) t = 256 / val; else @@ -310,7 +305,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, adis->spi->max_speed_hz = ADIS16260_SPI_FAST; ret = __adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); - mutex_unlock(&adis->state_lock); + adis_dev_unlock(adis); return ret; } return -EINVAL; diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index cec5e1f17c22..3e0734ddafe3 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -589,7 +589,7 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->dready_trig) return -ENOMEM; diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index b11ebd9bb7a4..17b939a367ad 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -98,7 +98,11 @@ struct bmg160_data { struct iio_trigger *motion_trig; struct iio_mount_matrix orientation; struct mutex mutex; - s16 buffer[8]; + /* Ensure naturally aligned timestamp */ + struct { + s16 chans[3]; + s64 timestamp __aligned(8); + } scan; u32 dps_range; int ev_enable_state; int slope_thres; @@ -882,12 +886,12 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); ret = regmap_bulk_read(data->regmap, BMG160_REG_XOUT_L, - data->buffer, AXIS_MAX * 2); + data->scan.chans, AXIS_MAX * 2); 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, pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -1102,8 +1106,7 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, 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; @@ -1137,14 +1140,14 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) return -ENOMEM; data->motion_trig = devm_iio_trigger_alloc(dev, "%s-any-motion-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->motion_trig) return -ENOMEM; diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index 645461c70454..410e5e9f2672 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -366,14 +366,7 @@ out_unlock: static int fxas21002c_pm_get(struct fxas21002c_data *data) { - struct device *dev = regmap_get_device(data->regmap); - int ret; - - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); - - return ret; + return pm_runtime_resume_and_get(regmap_get_device(data->regmap)); } static int fxas21002c_pm_put(struct fxas21002c_data *data) @@ -854,7 +847,7 @@ static int fxas21002c_trigger_probe(struct fxas21002c_data *data) data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) return -ENOMEM; @@ -1004,7 +997,6 @@ int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq, pm_disable: pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); return ret; } @@ -1018,7 +1010,6 @@ void fxas21002c_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); } EXPORT_SYMBOL_GPL(fxas21002c_core_remove); diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index af0aaa146f0c..04dd6a7969ea 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -114,7 +114,7 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev) struct itg3200 *st = iio_priv(indio_dev); st->trig = iio_trigger_alloc(&st->i2c->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->trig) return -ENOMEM; diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index e9804664db73..a7f1bbb5f289 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -308,8 +308,7 @@ static int itg3200_probe(struct i2c_client *client, st = iio_priv(indio_dev); - ret = iio_read_mount_matrix(&client->dev, "mount-matrix", - &st->orientation); + ret = iio_read_mount_matrix(&client->dev, &st->orientation); if (ret) return ret; diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index f17a93519535..3225de1f023b 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1058,7 +1058,7 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) mpu3050->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!mpu3050->trig) return -ENOMEM; @@ -1164,7 +1164,7 @@ int mpu3050_common_probe(struct device *dev, mpu3050->divisor = 99; /* Read the mounting matrix, if present */ - ret = iio_read_mount_matrix(dev, "mount-matrix", &mpu3050->orientation); + ret = iio_read_mount_matrix(dev, &mpu3050->orientation); if (ret) return ret; diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h index fd9171cc3aba..6537f5cb8320 100644 --- a/drivers/iio/gyro/st_gyro.h +++ b/drivers/iio/gyro/st_gyro.h @@ -24,18 +24,6 @@ #define LSM330_GYRO_DEV_NAME "lsm330_gyro" #define LSM9DS0_GYRO_DEV_NAME "lsm9ds0_gyro" -/** - * struct st_sensors_platform_data - gyro platform data - * @drdy_int_pin: DRDY on gyros is available only on INT2 pin. - */ -static __maybe_unused const struct st_sensors_platform_data gyro_pdata = { - .drdy_int_pin = 2, -}; - -const struct st_sensor_settings *st_gyro_get_settings(const char *name); -int st_gyro_common_probe(struct iio_dev *indio_dev); -void st_gyro_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_gyro_allocate_ring(struct iio_dev *indio_dev); void st_gyro_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index c8aa051995d3..b86ee4d940d9 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -37,19 +37,36 @@ #define ST_GYRO_FS_AVL_500DPS 500 #define ST_GYRO_FS_AVL_2000DPS 2000 +static const struct iio_mount_matrix * +st_gyro_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *gdata = iio_priv(indio_dev); + + return &gdata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info st_gyro_mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_gyro_get_mount_matrix), + { } +}; + static const struct iio_chan_spec st_gyro_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_GYRO_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + ST_GYRO_DEFAULT_OUT_X_L_ADDR, + st_gyro_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_GYRO_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + ST_GYRO_DEFAULT_OUT_Y_L_ADDR, + st_gyro_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_GYRO_DEFAULT_OUT_Z_L_ADDR), + ST_GYRO_DEFAULT_OUT_Z_L_ADDR, + st_gyro_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -357,6 +374,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { }, }; +/* DRDY on gyros is available only on INT2 pin */ +static const struct st_sensors_platform_data gyro_pdata = { + .drdy_int_pin = 2, +}; + static int st_gyro_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -466,18 +488,18 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &gyro_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_gyro_power_off; + return err; gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS; indio_dev->channels = gdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + err = iio_read_mount_matrix(gdata->dev, &gdata->mount_matrix); + if (err) + return err; + gdata->current_fullscale = &gdata->sensor_settings->fs.fs_avl[0]; gdata->odr = gdata->sensor_settings->odr.odr_avl[0].hz; @@ -485,11 +507,11 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_gyro_power_off; + return err; err = st_gyro_allocate_ring(indio_dev); if (err < 0) - goto st_gyro_power_off; + return err; if (gdata->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -512,9 +534,6 @@ st_gyro_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_gyro_probe_trigger_error: st_gyro_deallocate_ring(indio_dev); -st_gyro_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_gyro_common_probe); @@ -523,8 +542,6 @@ void st_gyro_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *gdata = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (gdata->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 8190966e6ff0..a25cc0379e16 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -86,16 +86,29 @@ static int st_gyro_i2c_probe(struct i2c_client *client, if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_gyro_common_probe(indio_dev); if (err < 0) - return err; + goto st_gyro_power_off; return 0; + +st_gyro_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_gyro_i2c_remove(struct i2c_client *client) { - st_gyro_common_remove(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + + st_gyro_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index efb862763ca3..18d6a2aeda45 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -90,16 +90,29 @@ static int st_gyro_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_gyro_common_probe(indio_dev); if (err < 0) - return err; + goto st_gyro_power_off; return 0; + +st_gyro_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_gyro_spi_remove(struct spi_device *spi) { - st_gyro_common_remove(spi_get_drvdata(spi)); + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + + st_gyro_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 1fa8d51d5080..d4921385aaf7 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -521,7 +521,7 @@ static int afe4403_probe(struct spi_device *spi) afe->trig = devm_iio_trigger_alloc(afe->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!afe->trig) { dev_err(afe->dev, "Unable to allocate IIO trigger\n"); ret = -ENOMEM; diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index e1476bf79fe2..d8a27dfe074a 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -528,7 +528,7 @@ static int afe4404_probe(struct i2c_client *client, afe->trig = devm_iio_trigger_alloc(afe->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!afe->trig) { dev_err(afe->dev, "Unable to allocate IIO trigger\n"); ret = -ENOMEM; diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 23bc9c784ef4..4a39f1019347 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -7,7 +7,6 @@ * 7-bit I2C address: 0x5C. */ -#include <linux/acpi.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/kernel.h> @@ -33,7 +32,11 @@ struct am2315_data { struct i2c_client *client; struct mutex lock; - s16 buffer[8]; /* 2x16-bit channels + 2x16 padding + 4x16 timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + s16 chans[2]; + s64 timestamp __aligned(8); + } scan; }; struct am2315_sensor_data { @@ -167,20 +170,20 @@ static irqreturn_t am2315_trigger_handler(int irq, void *p) mutex_lock(&data->lock); if (*(indio_dev->active_scan_mask) == AM2315_ALL_CHANNEL_MASK) { - data->buffer[0] = sensor_data.hum_data; - data->buffer[1] = sensor_data.temp_data; + data->scan.chans[0] = sensor_data.hum_data; + data->scan.chans[1] = sensor_data.temp_data; } else { i = 0; for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { - data->buffer[i] = (bit ? sensor_data.temp_data : - sensor_data.hum_data); + data->scan.chans[i] = (bit ? sensor_data.temp_data : + sensor_data.hum_data); i++; } } 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); @@ -256,17 +259,9 @@ static const struct i2c_device_id am2315_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); -static const struct acpi_device_id am2315_acpi_id[] = { - {"AOS2315", 0}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, am2315_acpi_id); - static struct i2c_driver am2315_driver = { .driver = { .name = "am2315", - .acpi_match_table = ACPI_PTR(am2315_acpi_id), }, .probe = am2315_probe, .id_table = am2315_i2c_id, diff --git a/drivers/iio/humidity/hdc2010.c b/drivers/iio/humidity/hdc2010.c index 83f5b9f60780..1381df46187c 100644 --- a/drivers/iio/humidity/hdc2010.c +++ b/drivers/iio/humidity/hdc2010.c @@ -272,7 +272,6 @@ static int hdc2010_probe(struct i2c_client *client, data->client = client; mutex_init(&data->lock); - indio_dev->dev.parent = &client->dev; /* * As DEVICE ID register does not differentiate between * HDC2010 and HDC2080, we have the name hardcoded diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index f02883b08480..001ca2c3ff95 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -94,6 +94,7 @@ config KMX61 source "drivers/iio/imu/inv_icm42600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" source "drivers/iio/imu/st_lsm6dsx/Kconfig" +source "drivers/iio/imu/st_lsm9ds0/Kconfig" endmenu diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 13e9ff442b11..c82748096c77 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -26,3 +26,4 @@ obj-y += inv_mpu6050/ obj-$(CONFIG_KMX61) += kmx61.o obj-y += st_lsm6dsx/ +obj-y += st_lsm9ds0/ diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 319b64b2fd88..a5b421f42287 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -12,14 +12,10 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/module.h> #include <asm/unaligned.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2) diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 768aa493a1a6..641b4f7d066d 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -10,22 +10,15 @@ * Copyright (c) 2011 Analog Devices Inc. */ -#include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/delay.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/debugfs.h> #include <linux/bitops.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/imu/adis.h> @@ -641,28 +634,13 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct adis16400_state *st = iio_priv(indio_dev); struct adis *adis = &st->adis; - u32 old_speed_hz = st->adis.spi->max_speed_hz; void *buffer; int ret; - if (!adis->buffer) - return -ENOMEM; - - if (!(st->variant->flags & ADIS16400_NO_BURST) && - st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) { - st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST; - spi_setup(st->adis.spi); - } - ret = spi_sync(adis->spi, &adis->msg); if (ret) dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret); - if (!(st->variant->flags & ADIS16400_NO_BURST)) { - st->adis.spi->max_speed_hz = old_speed_hz; - spi_setup(st->adis.spi); - } - if (st->variant->flags & ADIS16400_BURST_DIAG_STAT) buffer = adis->buffer + sizeof(u16); else @@ -968,7 +946,8 @@ static const char * const adis16400_status_error_msgs[] = { BIT(ADIS16400_DIAG_STAT_POWER_LOW), \ .timeouts = (_timeouts), \ .burst_reg_cmd = ADIS16400_GLOB_CMD, \ - .burst_len = (_burst_len) \ + .burst_len = (_burst_len), \ + .burst_max_speed_hz = ADIS16400_SPI_BURST \ } static const struct adis_timeout adis16300_timeouts = { diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 73bf45e859b8..ba373d7aaa2b 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -5,7 +5,6 @@ * Copyright 2019 Analog Devices Inc. */ -#include <linux/delay.h> #include <linux/module.h> #include <linux/spi/spi.h> diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 1de62fc79e0f..d0e84e5dee6c 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -14,7 +14,6 @@ #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> -#include <linux/iio/sysfs.h> #include <linux/iio/trigger_consumer.h> #include <linux/irq.h> #include <linux/lcm.h> @@ -645,7 +644,8 @@ static int adis16475_enable_irq(struct adis *adis, bool enable) .timeouts = (_timeouts), \ .burst_reg_cmd = ADIS16475_REG_GLOB_CMD, \ .burst_len = ADIS16475_BURST_MAX_DATA, \ - .burst_max_len = ADIS16475_BURST32_MAX_DATA \ + .burst_max_len = ADIS16475_BURST32_MAX_DATA, \ + .burst_max_speed_hz = ADIS16475_BURST_MAX_SPEED \ } static const struct adis16475_sync adis16475_sync_mode[] = { @@ -1062,15 +1062,11 @@ static irqreturn_t adis16475_trigger_handler(int irq, void *p) bool valid; /* offset until the first element after gyro and accel */ const u8 offset = st->burst32 ? 13 : 7; - const u32 cached_spi_speed_hz = adis->spi->max_speed_hz; - - adis->spi->max_speed_hz = ADIS16475_BURST_MAX_SPEED; ret = spi_sync(adis->spi, &adis->msg); if (ret) - return ret; + goto check_burst32; - adis->spi->max_speed_hz = cached_spi_speed_hz; buffer = adis->buffer; crc = be16_to_cpu(buffer[offset + 2]); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index f81b86690b76..9ec0e61b484f 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -9,21 +9,19 @@ #include <linux/bitfield.h> #include <linux/of_irq.h> #include <linux/interrupt.h> -#include <linux/delay.h> #include <linux/math.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/module.h> #include <linux/lcm.h> +#include <linux/swab.h> +#include <linux/crc32.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> +#include <linux/iio/trigger_consumer.h> #include <linux/debugfs.h> @@ -103,6 +101,12 @@ * Available only for ADIS1649x devices */ #define ADIS16495_REG_SYNC_SCALE ADIS16480_REG(0x03, 0x10) +#define ADIS16495_REG_BURST_CMD ADIS16480_REG(0x00, 0x7C) +#define ADIS16495_BURST_ID 0xA5A5 +/* total number of segments in burst */ +#define ADIS16495_BURST_MAX_DATA 20 +/* spi max speed in burst mode */ +#define ADIS16495_BURST_MAX_SPEED 6000000 #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) @@ -163,6 +167,8 @@ struct adis16480 { struct clk *ext_clk; enum adis16480_clock_mode clk_mode; unsigned int clk_freq; + /* Alignment needed for the timestamp */ + __be16 data[ADIS16495_BURST_MAX_DATA] __aligned(8); }; static const char * const adis16480_int_pin_names[4] = { @@ -863,7 +869,7 @@ static const char * const adis16480_status_error_msgs[] = { static int adis16480_enable_irq(struct adis *adis, bool enable); -#define ADIS16480_DATA(_prod_id, _timeouts) \ +#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len) \ { \ .diag_stat_reg = ADIS16480_REG_DIAG_STS, \ .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \ @@ -887,6 +893,9 @@ static int adis16480_enable_irq(struct adis *adis, bool enable); BIT(ADIS16480_DIAG_STAT_BARO_FAIL), \ .enable_irq = adis16480_enable_irq, \ .timeouts = (_timeouts), \ + .burst_reg_cmd = ADIS16495_REG_BURST_CMD, \ + .burst_len = (_burst_len), \ + .burst_max_speed_hz = ADIS16495_BURST_MAX_SPEED \ } static const struct adis_timeout adis16485_timeouts = { @@ -931,7 +940,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16375, &adis16485_timeouts), + .adis_data = ADIS16480_DATA(16375, &adis16485_timeouts, 0), }, [ADIS16480] = { .channels = adis16480_channels, @@ -944,7 +953,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16480, &adis16480_timeouts), + .adis_data = ADIS16480_DATA(16480, &adis16480_timeouts, 0), }, [ADIS16485] = { .channels = adis16485_channels, @@ -957,7 +966,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts), + .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0), }, [ADIS16488] = { .channels = adis16480_channels, @@ -970,7 +979,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts), + .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0), }, [ADIS16490] = { .channels = adis16485_channels, @@ -984,7 +993,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16490, &adis16495_timeouts), + .adis_data = ADIS16480_DATA(16490, &adis16495_timeouts, 0), }, [ADIS16495_1] = { .channels = adis16485_channels, @@ -998,7 +1007,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16495_2] = { .channels = adis16485_channels, @@ -1012,7 +1023,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16495_3] = { .channels = adis16485_channels, @@ -1026,7 +1039,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16497_1] = { .channels = adis16485_channels, @@ -1040,7 +1055,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16497_2] = { .channels = adis16485_channels, @@ -1054,7 +1071,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16497_3] = { .channels = adis16485_channels, @@ -1068,10 +1087,118 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, }; +static bool adis16480_validate_crc(const u16 *buf, const u8 n_elem, const u32 crc) +{ + u32 crc_calc; + u16 crc_buf[15]; + int j; + + for (j = 0; j < n_elem; j++) + crc_buf[j] = swab16(buf[j]); + + crc_calc = crc32(~0, crc_buf, n_elem * 2); + crc_calc ^= ~0; + + return (crc == crc_calc); +} + +static irqreturn_t adis16480_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adis16480 *st = iio_priv(indio_dev); + struct adis *adis = &st->adis; + int ret, bit, offset, i = 0; + __be16 *buffer; + u32 crc; + bool valid; + + adis_dev_lock(adis); + if (adis->current_page != 0) { + adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); + adis->tx[1] = 0; + ret = spi_write(adis->spi, adis->tx, 2); + if (ret) { + dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); + adis_dev_unlock(adis); + goto irq_done; + } + + adis->current_page = 0; + } + + ret = spi_sync(adis->spi, &adis->msg); + if (ret) { + dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret); + adis_dev_unlock(adis); + goto irq_done; + } + + adis_dev_unlock(adis); + + /* + * After making the burst request, the response can have one or two + * 16-bit responses containing the BURST_ID depending on the sclk. If + * clk > 3.6MHz, then we will have two BURST_ID in a row. If clk < 3MHZ, + * we have only one. To manage that variation, we use the transition from the + * BURST_ID to the SYS_E_FLAG register, which will not be equal to 0xA5A5. If + * we not find this variation in the first 4 segments, then the data should + * not be valid. + */ + buffer = adis->buffer; + for (offset = 0; offset < 4; offset++) { + u16 curr = be16_to_cpu(buffer[offset]); + u16 next = be16_to_cpu(buffer[offset + 1]); + + if (curr == ADIS16495_BURST_ID && next != ADIS16495_BURST_ID) { + offset++; + break; + } + } + + if (offset == 4) { + dev_err(&adis->spi->dev, "Invalid burst data\n"); + goto irq_done; + } + + crc = be16_to_cpu(buffer[offset + 16]) << 16 | be16_to_cpu(buffer[offset + 15]); + valid = adis16480_validate_crc((u16 *)&buffer[offset], 15, crc); + if (!valid) { + dev_err(&adis->spi->dev, "Invalid crc\n"); + goto irq_done; + } + + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { + /* + * When burst mode is used, temperature is the first data + * channel in the sequence, but the temperature scan index + * is 10. + */ + switch (bit) { + case ADIS16480_SCAN_TEMP: + st->data[i++] = buffer[offset + 1]; + break; + case ADIS16480_SCAN_GYRO_X ... ADIS16480_SCAN_ACCEL_Z: + /* The lower register data is sequenced first */ + st->data[i++] = buffer[2 * bit + offset + 3]; + st->data[i++] = buffer[2 * bit + offset + 2]; + break; + } + } + + iio_push_to_buffers_with_timestamp(indio_dev, st->data, pf->timestamp); +irq_done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const struct iio_info adis16480_info = { .read_raw = &adis16480_read_raw, .write_raw = &adis16480_write_raw, @@ -1341,7 +1468,8 @@ static int adis16480_probe(struct spi_device *spi) st->clk_freq = st->chip_info->int_clk; } - ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); + ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, + adis16480_trigger_handler); if (ret) return ret; diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index ac354321f63a..351c303c8a8c 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -51,9 +51,13 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, adis->xfer[0].tx_buf = tx; adis->xfer[0].bits_per_word = 8; adis->xfer[0].len = 2; + if (adis->data->burst_max_speed_hz) + adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz; adis->xfer[1].rx_buf = adis->buffer; adis->xfer[1].bits_per_word = 8; adis->xfer[1].len = burst_length; + if (adis->data->burst_max_speed_hz) + adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz; spi_message_init(&adis->msg); spi_message_add_tail(&adis->xfer[0], &adis->msg); @@ -129,31 +133,34 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) struct adis *adis = iio_device_get_drvdata(indio_dev); int ret; - if (!adis->buffer) - return -ENOMEM; - if (adis->data->has_paging) { mutex_lock(&adis->state_lock); if (adis->current_page != 0) { adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); adis->tx[1] = 0; - spi_write(adis->spi, adis->tx, 2); + ret = spi_write(adis->spi, adis->tx, 2); + if (ret) { + dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); + mutex_unlock(&adis->state_lock); + goto irq_done; + } + + adis->current_page = 0; } } ret = spi_sync(adis->spi, &adis->msg); - if (ret) - dev_err(&adis->spi->dev, "Failed to read data: %d", ret); - - - if (adis->data->has_paging) { - adis->current_page = 0; + if (adis->data->has_paging) mutex_unlock(&adis->state_lock); + if (ret) { + dev_err(&adis->spi->dev, "Failed to read data: %d", ret); + goto irq_done; } iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, pf->timestamp); +irq_done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index fa5540fabacc..48eedc29b28a 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -62,7 +62,8 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) int ret; adis->trig = devm_iio_trigger_alloc(&adis->spi->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!adis->trig) return -ENOMEM; diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 290b5ef83f77..824b5124a5f5 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -785,7 +785,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type) int ret; data->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (data->trig == NULL) return -ENOMEM; @@ -851,8 +852,7 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap, return ret; } - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 8bd77185ccb7..86858da9cc38 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -592,7 +592,7 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, st->chip = chip; st->map = regmap; - ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation); + ret = iio_read_mount_matrix(dev, &st->orientation); if (ret) { dev_err(dev, "failed to retrieve mounting matrix %d\n", ret); return ret; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 6244a07048df..64704b55f6eb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -1455,8 +1455,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, pdata = dev_get_platdata(dev); if (!pdata) { - result = iio_read_mount_matrix(dev, "mount-matrix", - &st->orientation); + result = iio_read_mount_matrix(dev, &st->orientation); if (result) { dev_err(dev, "Failed to retrieve mounting matrix %d\n", result); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index de8ed1446d60..e21ba778595a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -238,7 +238,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type) st->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->trig) return -ENOMEM; diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index fc5a60fcfec0..1dabfd615dab 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -750,7 +750,7 @@ static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device) } 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); @@ -759,8 +759,6 @@ static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device) dev_err(&data->client->dev, "Failed: kmx61_set_power_state for %d, ret %d\n", on, ret); - if (on) - pm_runtime_put_noidle(&data->client->dev); return ret; } @@ -1264,7 +1262,7 @@ static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data, "%s-%s-dev%d", indio_dev->name, tag, - indio_dev->id); + iio_device_id(indio_dev)); if (!trig) return ERR_PTR(-ENOMEM); @@ -1426,7 +1424,6 @@ static int kmx61_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); if (client->irq > 0) { iio_triggered_buffer_cleanup(data->acc_indio_dev); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 7cedaab096a7..db45f1fc0b81 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -15,19 +15,19 @@ * * Supported sensors: * - LSM6DS3: - * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 8KB * * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C: - * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 4KB * * - LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP: - * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416, + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, * 833 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 @@ -2256,7 +2256,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - err = iio_read_mount_matrix(hw->dev, "mount-matrix", &hw->orientation); + err = iio_read_mount_matrix(hw->dev, &hw->orientation); if (err) return err; diff --git a/drivers/iio/imu/st_lsm9ds0/Kconfig b/drivers/iio/imu/st_lsm9ds0/Kconfig new file mode 100644 index 000000000000..53b7017014f8 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config IIO_ST_LSM9DS0 + tristate "STMicroelectronics LSM9DS0 IMU driver" + depends on (I2C || SPI_MASTER) && SYSFS + depends on !SENSORS_LIS3_I2C + depends on !SENSORS_LIS3_SPI + select IIO_ST_LSM9DS0_I2C if I2C + select IIO_ST_LSM9DS0_SPI if SPI_MASTER + select IIO_ST_ACCEL_3AXIS + select IIO_ST_MAGN_3AXIS + + help + Say yes here to build support for STMicroelectronics LSM9DS0 IMU + sensor. Supported devices: accelerometer/magnetometer of lsm9ds0. + + To compile this driver as a module, choose M here: the module + will be called st_lsm9ds0. + +config IIO_ST_LSM9DS0_I2C + tristate + depends on IIO_ST_LSM9DS0 + select REGMAP_I2C + +config IIO_ST_LSM9DS0_SPI + tristate + depends on IIO_ST_LSM9DS0 + select REGMAP_SPI diff --git a/drivers/iio/imu/st_lsm9ds0/Makefile b/drivers/iio/imu/st_lsm9ds0/Makefile new file mode 100644 index 000000000000..488af523f648 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_IIO_ST_LSM9DS0) += st_lsm9ds0.o +st_lsm9ds0-y := st_lsm9ds0_core.o +obj-$(CONFIG_IIO_ST_LSM9DS0_I2C) += st_lsm9ds0_i2c.o +obj-$(CONFIG_IIO_ST_LSM9DS0_SPI) += st_lsm9ds0_spi.o diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h new file mode 100644 index 000000000000..146393afd9a7 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +// STMicroelectronics LSM9DS0 IMU driver + +#ifndef ST_LSM9DS0_H +#define ST_LSM9DS0_H + +struct iio_dev; +struct regulator; + +struct st_lsm9ds0 { + struct device *dev; + const char *name; + int irq; + struct iio_dev *accel; + struct iio_dev *magn; + struct regulator *vdd; + struct regulator *vdd_io; +}; + +int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap); +int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0); + +#endif /* ST_LSM9DS0_H */ diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c new file mode 100644 index 000000000000..8204f7303fd7 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics LSM9DS0 IMU driver + * + * Copyright (C) 2021, Intel Corporation + * + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/common/st_sensors.h> +#include <linux/iio/iio.h> + +#include "st_lsm9ds0.h" + +static int st_lsm9ds0_power_enable(struct device *dev, struct st_lsm9ds0 *lsm9ds0) +{ + int ret; + + /* Regulators not mandatory, but if requested we should enable them. */ + lsm9ds0->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(lsm9ds0->vdd)) { + dev_err(dev, "unable to get Vdd supply\n"); + return PTR_ERR(lsm9ds0->vdd); + } + ret = regulator_enable(lsm9ds0->vdd); + if (ret) { + dev_warn(dev, "Failed to enable specified Vdd supply\n"); + return ret; + } + + lsm9ds0->vdd_io = devm_regulator_get(dev, "vddio"); + if (IS_ERR(lsm9ds0->vdd_io)) { + dev_err(dev, "unable to get Vdd_IO supply\n"); + regulator_disable(lsm9ds0->vdd); + return PTR_ERR(lsm9ds0->vdd_io); + } + ret = regulator_enable(lsm9ds0->vdd_io); + if (ret) { + dev_warn(dev, "Failed to enable specified Vdd_IO supply\n"); + regulator_disable(lsm9ds0->vdd); + return ret; + } + + return 0; +} + +static void st_lsm9ds0_power_disable(void *data) +{ + struct st_lsm9ds0 *lsm9ds0 = data; + + regulator_disable(lsm9ds0->vdd_io); + regulator_disable(lsm9ds0->vdd); +} + +static int devm_st_lsm9ds0_power_enable(struct st_lsm9ds0 *lsm9ds0) +{ + struct device *dev = lsm9ds0->dev; + int ret; + + ret = st_lsm9ds0_power_enable(dev, lsm9ds0); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, st_lsm9ds0_power_disable, lsm9ds0); +} + +static int st_lsm9ds0_probe_accel(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) +{ + const struct st_sensor_settings *settings; + struct device *dev = lsm9ds0->dev; + struct st_sensor_data *data; + + settings = st_accel_get_settings(lsm9ds0->name); + if (!settings) { + dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name); + return -ENODEV; + } + + lsm9ds0->accel = devm_iio_device_alloc(dev, sizeof(*data)); + if (!lsm9ds0->accel) + return -ENOMEM; + + lsm9ds0->accel->name = lsm9ds0->name; + + data = iio_priv(lsm9ds0->accel); + data->sensor_settings = (struct st_sensor_settings *)settings; + data->dev = dev; + data->irq = lsm9ds0->irq; + data->regmap = regmap; + data->vdd = lsm9ds0->vdd; + data->vdd_io = lsm9ds0->vdd_io; + + return st_accel_common_probe(lsm9ds0->accel); +} + +static int st_lsm9ds0_probe_magn(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) +{ + const struct st_sensor_settings *settings; + struct device *dev = lsm9ds0->dev; + struct st_sensor_data *data; + + settings = st_magn_get_settings(lsm9ds0->name); + if (!settings) { + dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name); + return -ENODEV; + } + + lsm9ds0->magn = devm_iio_device_alloc(dev, sizeof(*data)); + if (!lsm9ds0->magn) + return -ENOMEM; + + lsm9ds0->magn->name = lsm9ds0->name; + + data = iio_priv(lsm9ds0->magn); + data->sensor_settings = (struct st_sensor_settings *)settings; + data->dev = dev; + data->irq = lsm9ds0->irq; + data->regmap = regmap; + data->vdd = lsm9ds0->vdd; + data->vdd_io = lsm9ds0->vdd_io; + + return st_magn_common_probe(lsm9ds0->magn); +} + +int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) +{ + int ret; + + ret = devm_st_lsm9ds0_power_enable(lsm9ds0); + if (ret) + return ret; + + /* Setup accelerometer device */ + ret = st_lsm9ds0_probe_accel(lsm9ds0, regmap); + if (ret) + return ret; + + /* Setup magnetometer device */ + ret = st_lsm9ds0_probe_magn(lsm9ds0, regmap); + if (ret) + st_accel_common_remove(lsm9ds0->accel); + + return ret; +} +EXPORT_SYMBOL_GPL(st_lsm9ds0_probe); + +int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0) +{ + st_magn_common_remove(lsm9ds0->magn); + st_accel_common_remove(lsm9ds0->accel); + + return 0; +} +EXPORT_SYMBOL_GPL(st_lsm9ds0_remove); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c new file mode 100644 index 000000000000..50a36ab53bc3 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics LSM9DS0 IMU driver + * + * Copyright (C) 2021, Intel Corporation + * + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/iio/common/st_sensors_i2c.h> + +#include "st_lsm9ds0.h" + +static const struct of_device_id st_lsm9ds0_of_match[] = { + { + .compatible = "st,lsm9ds0-imu", + .data = LSM9DS0_IMU_DEV_NAME, + }, + {} +}; +MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); + +static const struct i2c_device_id st_lsm9ds0_id_table[] = { + { LSM9DS0_IMU_DEV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(i2c, st_lsm9ds0_id_table); + +static const struct regmap_config st_lsm9ds0_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x80, +}; + +static int st_lsm9ds0_i2c_probe(struct i2c_client *client) +{ + const struct regmap_config *config = &st_lsm9ds0_regmap_config; + struct device *dev = &client->dev; + struct st_lsm9ds0 *lsm9ds0; + struct regmap *regmap; + + st_sensors_dev_name_probe(dev, client->name, sizeof(client->name)); + + lsm9ds0 = devm_kzalloc(dev, sizeof(*lsm9ds0), GFP_KERNEL); + if (!lsm9ds0) + return -ENOMEM; + + lsm9ds0->dev = dev; + lsm9ds0->name = client->name; + lsm9ds0->irq = client->irq; + + regmap = devm_regmap_init_i2c(client, config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + i2c_set_clientdata(client, lsm9ds0); + + return st_lsm9ds0_probe(lsm9ds0, regmap); +} + +static int st_lsm9ds0_i2c_remove(struct i2c_client *client) +{ + return st_lsm9ds0_remove(i2c_get_clientdata(client)); +} + +static struct i2c_driver st_lsm9ds0_driver = { + .driver = { + .name = "st-lsm9ds0-i2c", + .of_match_table = st_lsm9ds0_of_match, + }, + .probe_new = st_lsm9ds0_i2c_probe, + .remove = st_lsm9ds0_i2c_remove, + .id_table = st_lsm9ds0_id_table, +}; +module_i2c_driver(st_lsm9ds0_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c new file mode 100644 index 000000000000..272c88990dd0 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics LSM9DS0 IMU driver + * + * Copyright (C) 2021, Intel Corporation + * + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include <linux/iio/common/st_sensors_spi.h> + +#include "st_lsm9ds0.h" + +static const struct of_device_id st_lsm9ds0_of_match[] = { + { + .compatible = "st,lsm9ds0-imu", + .data = LSM9DS0_IMU_DEV_NAME, + }, + {} +}; +MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); + +static const struct spi_device_id st_lsm9ds0_id_table[] = { + { LSM9DS0_IMU_DEV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table); + +static const struct regmap_config st_lsm9ds0_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0xc0, +}; + +static int st_lsm9ds0_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct st_lsm9ds0 *lsm9ds0; + struct regmap *regmap; + + st_sensors_dev_name_probe(dev, spi->modalias, sizeof(spi->modalias)); + + lsm9ds0 = devm_kzalloc(dev, sizeof(*lsm9ds0), GFP_KERNEL); + if (!lsm9ds0) + return -ENOMEM; + + lsm9ds0->dev = dev; + lsm9ds0->name = spi->modalias; + lsm9ds0->irq = spi->irq; + + regmap = devm_regmap_init_spi(spi, &st_lsm9ds0_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + spi_set_drvdata(spi, lsm9ds0); + + return st_lsm9ds0_probe(lsm9ds0, regmap); +} + +static int st_lsm9ds0_spi_remove(struct spi_device *spi) +{ + return st_lsm9ds0_remove(spi_get_drvdata(spi)); +} + +static struct spi_driver st_lsm9ds0_driver = { + .driver = { + .name = "st-lsm9ds0-spi", + .of_match_table = st_lsm9ds0_of_match, + }, + .probe = st_lsm9ds0_spi_probe, + .remove = st_lsm9ds0_spi_remove, + .id_table = st_lsm9ds0_id_table, +}; +module_spi_driver(st_lsm9ds0_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 9a8e16c7e9af..fdd623407b96 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -601,8 +601,10 @@ static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev, static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + return iio_storage_bytes_for_si(indio_dev, - indio_dev->scan_index_timestamp); + iio_dev_opaque->scan_index_timestamp); } static int iio_compute_scan_bytes(struct iio_dev *indio_dev, @@ -924,7 +926,6 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, if (ret) goto error_clear_mux_table; out_loc += length; - in_loc += length; } buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL); if (buffer->demux_bounce == NULL) { @@ -1148,12 +1149,13 @@ int iio_update_buffers(struct iio_dev *indio_dev, struct iio_buffer *insert_buffer, struct iio_buffer *remove_buffer) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); int ret; if (insert_buffer == remove_buffer) return 0; - mutex_lock(&indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); mutex_lock(&indio_dev->mlock); if (insert_buffer && iio_buffer_is_active(insert_buffer)) @@ -1176,7 +1178,7 @@ int iio_update_buffers(struct iio_dev *indio_dev, out_unlock: mutex_unlock(&indio_dev->mlock); - mutex_unlock(&indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -1469,6 +1471,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, struct iio_dev *indio_dev, int index) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_dev_attr *p; struct attribute **attr; int ret, i, attrn, scan_el_attrcount, buffer_attrcount; @@ -1495,7 +1498,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, goto error_cleanup_dynamic; scan_el_attrcount += ret; if (channels[i].type == IIO_TIMESTAMP) - indio_dev->scan_index_timestamp = + iio_dev_opaque->scan_index_timestamp = channels[i].scan_index; } if (indio_dev->masklength && buffer->scan_mask == NULL) { diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 59efb36db2c7..6d2175eb7af2 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -169,6 +169,20 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type", [IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient", }; +/** + * iio_device_id() - query the unique ID for the device + * @indio_dev: Device structure whose ID is being queried + * + * The IIO device ID is a unique index used for example for the naming + * of the character device /dev/iio\:device[ID] + */ +int iio_device_id(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + return iio_dev_opaque->id; +} +EXPORT_SYMBOL_GPL(iio_device_id); /** * iio_sysfs_match_string_with_gaps - matches given string in an array with gaps @@ -257,7 +271,7 @@ int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) mutex_unlock(&indio_dev->mlock); return -EBUSY; } - indio_dev->clock_id = clock_id; + iio_dev_opaque->clock_id = clock_id; mutex_unlock(&indio_dev->mlock); return 0; @@ -265,6 +279,18 @@ int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) EXPORT_SYMBOL(iio_device_set_clock); /** + * iio_device_get_clock() - Retrieve current timestamping clock for the device + * @indio_dev: IIO device structure containing the device + */ +clockid_t iio_device_get_clock(const struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + return iio_dev_opaque->clock_id; +} +EXPORT_SYMBOL(iio_device_get_clock); + +/** * iio_get_time_ns() - utility function to get a time stamp for events etc * @indio_dev: device */ @@ -591,7 +617,6 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix); * iio_read_mount_matrix() - retrieve iio device mounting matrix from * device "mount-matrix" property * @dev: device the mounting matrix property is assigned to - * @propname: device specific mounting matrix property name * @matrix: where to store retrieved matrix * * If device is assigned no mounting matrix property, a default 3x3 identity @@ -599,14 +624,12 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix); * * Return: 0 if success, or a negative error code on failure. */ -int iio_read_mount_matrix(struct device *dev, const char *propname, - struct iio_mount_matrix *matrix) +int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix) { size_t len = ARRAY_SIZE(iio_mount_idmatrix.rotation); int err; - err = device_property_read_string_array(dev, propname, - matrix->rotation, len); + err = device_property_read_string_array(dev, "mount-matrix", matrix->rotation, len); if (err == len) return 0; @@ -1588,7 +1611,7 @@ static void iio_dev_release(struct device *device) iio_device_detach_buffers(indio_dev); - ida_simple_remove(&iio_ida, indio_dev->id); + ida_simple_remove(&iio_ida, iio_dev_opaque->id); kfree(iio_dev_opaque); } @@ -1628,17 +1651,17 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) device_initialize(&indio_dev->dev); iio_device_set_drvdata(indio_dev, (void *)indio_dev); mutex_init(&indio_dev->mlock); - mutex_init(&indio_dev->info_exist_lock); + mutex_init(&iio_dev_opaque->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); - indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); - if (indio_dev->id < 0) { + iio_dev_opaque->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); + if (iio_dev_opaque->id < 0) { /* cannot use a dev_err as the name isn't available */ pr_err("failed to get device id\n"); kfree(iio_dev_opaque); return NULL; } - dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id); + dev_set_name(&indio_dev->dev, "iio:device%d", iio_dev_opaque->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); @@ -1657,9 +1680,9 @@ void iio_device_free(struct iio_dev *dev) } EXPORT_SYMBOL(iio_device_free); -static void devm_iio_device_release(struct device *dev, void *res) +static void devm_iio_device_release(void *iio_dev) { - iio_device_free(*(struct iio_dev **)res); + iio_device_free(iio_dev); } /** @@ -1675,20 +1698,17 @@ static void devm_iio_device_release(struct device *dev, void *res) */ struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv) { - struct iio_dev **ptr, *iio_dev; + struct iio_dev *iio_dev; + int ret; - ptr = devres_alloc(devm_iio_device_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) + iio_dev = iio_device_alloc(parent, sizeof_priv); + if (!iio_dev) return NULL; - iio_dev = iio_device_alloc(parent, sizeof_priv); - if (iio_dev) { - *ptr = iio_dev; - devres_add(parent, ptr); - } else { - devres_free(ptr); - } + ret = devm_add_action_or_reset(parent, devm_iio_device_release, + iio_dev); + if (ret) + return NULL; return iio_dev; } @@ -1704,11 +1724,12 @@ EXPORT_SYMBOL_GPL(devm_iio_device_alloc); **/ static int iio_chrdev_open(struct inode *inode, struct file *filp) { - struct iio_dev *indio_dev = container_of(inode->i_cdev, - struct iio_dev, chrdev); + struct iio_dev_opaque *iio_dev_opaque = + container_of(inode->i_cdev, struct iio_dev_opaque, chrdev); + struct iio_dev *indio_dev = &iio_dev_opaque->indio_dev; struct iio_dev_buffer_pair *ib; - if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags)) + if (test_and_set_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags)) return -EBUSY; iio_device_get(indio_dev); @@ -1716,7 +1737,7 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) ib = kmalloc(sizeof(*ib), GFP_KERNEL); if (!ib) { iio_device_put(indio_dev); - clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); + clear_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags); return -ENOMEM; } @@ -1738,10 +1759,11 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) static int iio_chrdev_release(struct inode *inode, struct file *filp) { struct iio_dev_buffer_pair *ib = filp->private_data; - struct iio_dev *indio_dev = container_of(inode->i_cdev, - struct iio_dev, chrdev); + struct iio_dev_opaque *iio_dev_opaque = + container_of(inode->i_cdev, struct iio_dev_opaque, chrdev); + struct iio_dev *indio_dev = &iio_dev_opaque->indio_dev; kfree(ib); - clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); + clear_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags); iio_device_put(indio_dev); return 0; @@ -1768,7 +1790,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct iio_ioctl_handler *h; int ret = -ENODEV; - mutex_lock(&indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); /** * The NULL check here is required to prevent crashing when a device @@ -1788,7 +1810,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -ENODEV; out_unlock: - mutex_unlock(&indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -1847,7 +1869,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) if (!indio_dev->info) return -EINVAL; - indio_dev->driver_module = this_mod; + iio_dev_opaque->driver_module = this_mod; /* If the calling driver did not initialize of_node, do it here */ if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; @@ -1889,19 +1911,19 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) indio_dev->setup_ops = &noop_ring_setup_ops; if (iio_dev_opaque->attached_buffers_cnt) - cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + cdev_init(&iio_dev_opaque->chrdev, &iio_buffer_fileops); else if (iio_dev_opaque->event_interface) - cdev_init(&indio_dev->chrdev, &iio_event_fileops); + cdev_init(&iio_dev_opaque->chrdev, &iio_event_fileops); if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) { - indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); - indio_dev->chrdev.owner = this_mod; + indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), iio_dev_opaque->id); + iio_dev_opaque->chrdev.owner = this_mod; } /* assign device groups now; they should be all registered now */ indio_dev->dev.groups = iio_dev_opaque->groups; - ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); + ret = cdev_device_add(&iio_dev_opaque->chrdev, &indio_dev->dev); if (ret < 0) goto error_unreg_eventset; @@ -1925,9 +1947,11 @@ EXPORT_SYMBOL(__iio_device_register); **/ void iio_device_unregister(struct iio_dev *indio_dev) { - cdev_device_del(&indio_dev->chrdev, &indio_dev->dev); + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - mutex_lock(&indio_dev->info_exist_lock); + cdev_device_del(&iio_dev_opaque->chrdev, &indio_dev->dev); + + mutex_lock(&iio_dev_opaque->info_exist_lock); iio_device_unregister_debugfs(indio_dev); @@ -1938,35 +1962,27 @@ void iio_device_unregister(struct iio_dev *indio_dev) iio_device_wakeup_eventset(indio_dev); iio_buffer_wakeup_poll(indio_dev); - mutex_unlock(&indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); iio_buffers_free_sysfs_and_mask(indio_dev); } EXPORT_SYMBOL(iio_device_unregister); -static void devm_iio_device_unreg(struct device *dev, void *res) +static void devm_iio_device_unreg(void *indio_dev) { - iio_device_unregister(*(struct iio_dev **)res); + iio_device_unregister(indio_dev); } int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, struct module *this_mod) { - struct iio_dev **ptr; int ret; - ptr = devres_alloc(devm_iio_device_unreg, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - *ptr = indio_dev; ret = __iio_device_register(indio_dev, this_mod); - 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_device_unreg, indio_dev); } EXPORT_SYMBOL_GPL(__devm_iio_device_register); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index b2c94abbb487..b23caa2f2aa1 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/iio/iio.h> +#include <linux/iio/iio-opaque.h> #include <linux/iio/trigger.h> #include "iio_core.h" #include "iio_core_trigger.h" @@ -116,14 +117,17 @@ EXPORT_SYMBOL(iio_trigger_unregister); int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig) { + struct iio_dev_opaque *iio_dev_opaque; + if (!indio_dev || !trig) return -EINVAL; + iio_dev_opaque = to_iio_dev_opaque(indio_dev); mutex_lock(&indio_dev->mlock); - WARN_ON(indio_dev->trig_readonly); + WARN_ON(iio_dev_opaque->trig_readonly); indio_dev->trig = iio_trigger_get(trig); - indio_dev->trig_readonly = true; + iio_dev_opaque->trig_readonly = true; mutex_unlock(&indio_dev->mlock); return 0; @@ -240,12 +244,13 @@ static void iio_trigger_put_irq(struct iio_trigger *trig, int irq) int iio_trigger_attach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(pf->indio_dev); bool notinuse = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); int ret = 0; /* Prevent the module from being removed whilst attached to a trigger */ - __module_get(pf->indio_dev->driver_module); + __module_get(iio_dev_opaque->driver_module); /* Get irq number */ pf->irq = iio_trigger_get_irq(trig); @@ -284,13 +289,14 @@ out_free_irq: out_put_irq: iio_trigger_put_irq(trig, pf->irq); out_put_module: - module_put(pf->indio_dev->driver_module); + module_put(iio_dev_opaque->driver_module); return ret; } int iio_trigger_detach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(pf->indio_dev); bool no_other_users = bitmap_weight(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER) == 1; int ret = 0; @@ -304,7 +310,7 @@ int iio_trigger_detach_poll_func(struct iio_trigger *trig, trig->attached_own_device = false; iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); - module_put(pf->indio_dev->driver_module); + module_put(iio_dev_opaque->driver_module); return ret; } @@ -399,6 +405,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_trigger *oldtrig = indio_dev->trig; struct iio_trigger *trig; int ret; @@ -408,7 +415,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, mutex_unlock(&indio_dev->mlock); return -EBUSY; } - if (indio_dev->trig_readonly) { + if (iio_dev_opaque->trig_readonly) { mutex_unlock(&indio_dev->mlock); return -EPERM; } @@ -634,9 +641,9 @@ struct iio_trigger *devm_iio_trigger_alloc(struct device *parent, const char *fm } EXPORT_SYMBOL_GPL(devm_iio_trigger_alloc); -static void devm_iio_trigger_unreg(struct device *dev, void *res) +static void devm_iio_trigger_unreg(void *trigger_info) { - iio_trigger_unregister(*(struct iio_trigger **)res); + iio_trigger_unregister(trigger_info); } /** @@ -657,21 +664,13 @@ int __devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info, struct module *this_mod) { - struct iio_trigger **ptr; int ret; - ptr = devres_alloc(devm_iio_trigger_unreg, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - *ptr = trig_info; ret = __iio_trigger_register(trig_info, this_mod); - 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_trigger_unreg, trig_info); } EXPORT_SYMBOL_GPL(__devm_iio_trigger_register); diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c index 53da9ab17a62..4bedc65c9fe3 100644 --- a/drivers/iio/industrialio-triggered-event.c +++ b/drivers/iio/industrialio-triggered-event.c @@ -37,7 +37,7 @@ int iio_triggered_event_setup(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_event == NULL) return -ENOMEM; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9c22697b7e83..391a3380a1d1 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -10,6 +10,7 @@ #include <linux/of.h> #include <linux/iio/iio.h> +#include <linux/iio/iio-opaque.h> #include "iio_core.h" #include <linux/iio/machine.h> #include <linux/iio/driver.h> @@ -359,30 +360,24 @@ void iio_channel_release(struct iio_channel *channel) } EXPORT_SYMBOL_GPL(iio_channel_release); -static void devm_iio_channel_free(struct device *dev, void *res) +static void devm_iio_channel_free(void *iio_channel) { - struct iio_channel *channel = *(struct iio_channel **)res; - - iio_channel_release(channel); + iio_channel_release(iio_channel); } struct iio_channel *devm_iio_channel_get(struct device *dev, const char *channel_name) { - struct iio_channel **ptr, *channel; - - ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct iio_channel *channel; + int ret; channel = iio_channel_get(dev, channel_name); - if (IS_ERR(channel)) { - devres_free(ptr); + if (IS_ERR(channel)) return channel; - } - *ptr = channel; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); + if (ret) + return ERR_PTR(ret); return channel; } @@ -392,20 +387,16 @@ struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, struct device_node *np, const char *channel_name) { - struct iio_channel **ptr, *channel; - - ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct iio_channel *channel; + int ret; channel = of_iio_channel_get_by_name(np, channel_name); - if (IS_ERR(channel)) { - devres_free(ptr); + if (IS_ERR(channel)) return channel; - } - *ptr = channel; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); + if (ret) + return ERR_PTR(ret); return channel; } @@ -496,29 +487,24 @@ void iio_channel_release_all(struct iio_channel *channels) } EXPORT_SYMBOL_GPL(iio_channel_release_all); -static void devm_iio_channel_free_all(struct device *dev, void *res) +static void devm_iio_channel_free_all(void *iio_channels) { - struct iio_channel *channels = *(struct iio_channel **)res; - - iio_channel_release_all(channels); + iio_channel_release_all(iio_channels); } struct iio_channel *devm_iio_channel_get_all(struct device *dev) { - struct iio_channel **ptr, *channels; - - ptr = devres_alloc(devm_iio_channel_free_all, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct iio_channel *channels; + int ret; channels = iio_channel_get_all(dev); - if (IS_ERR(channels)) { - devres_free(ptr); + if (IS_ERR(channels)) return channels; - } - *ptr = channels; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_iio_channel_free_all, + channels); + if (ret) + return ERR_PTR(ret); return channels; } @@ -553,9 +539,10 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, int iio_read_channel_raw(struct iio_channel *chan, int *val) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -563,7 +550,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -571,9 +558,10 @@ EXPORT_SYMBOL_GPL(iio_read_channel_raw); int iio_read_channel_average_raw(struct iio_channel *chan, int *val) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -581,7 +569,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -646,9 +634,10 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, int *processed, unsigned int scale) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -657,7 +646,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed, scale); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -666,9 +655,10 @@ EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum attribute) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -676,7 +666,7 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, ret = iio_channel_read(chan, val, val2, attribute); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -691,9 +681,10 @@ EXPORT_SYMBOL_GPL(iio_read_channel_offset); int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, unsigned int scale) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -714,7 +705,7 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, } err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -748,9 +739,10 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, const int **vals, int *type, int *length, enum iio_chan_info_enum attribute) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; @@ -758,7 +750,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, ret = iio_channel_read_avail(chan, vals, type, length, attribute); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -830,10 +822,11 @@ static int iio_channel_read_max(struct iio_channel *chan, int iio_read_max_channel_raw(struct iio_channel *chan, int *val) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; int type; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; @@ -841,7 +834,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -849,10 +842,11 @@ EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret = 0; /* Need to verify underlying driver has not gone away */ - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -860,7 +854,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) *type = chan->channel->type; err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -876,9 +870,10 @@ static int iio_channel_write(struct iio_channel *chan, int val, int val2, int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, enum iio_chan_info_enum attribute) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -886,7 +881,7 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, ret = iio_channel_write(chan, val, val2, attribute); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 917f9becf9c7..a62c7b4b8678 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -499,6 +499,17 @@ config TSL2583 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. Access ALS data via iio, sysfs. +config TSL2591 + tristate "TAOS TSL2591 ambient light sensor" + depends on I2C + help + Select Y here for support of the AMS/TAOS TSL2591 ambient light sensor, + featuring channels for combined visible + IR intensity and lux illuminance. + Access data via iio and sysfs. Supports iio_events. + + To compile this driver as a module, select M: the + module will be called tsl2591. + config TSL2772 tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index ea376deaca54..d10912faf964 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL2583) += tsl2583.o +obj-$(CONFIG_TSL2591) += tsl2591.o obj-$(CONFIG_TSL2772) += tsl2772.o obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 0a6ab5761eec..e1ff6f524f4b 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -204,7 +204,8 @@ static int acpi_als_add(struct acpi_device *device) indio_dev->channels = acpi_als_channels; indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); - als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); + als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); if (!als->trig) return -ENOMEM; diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 2f8b494f3e08..9de3262aa688 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -339,9 +339,7 @@ static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -647,7 +645,6 @@ static int isl29028_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return isl29028_clear_configure_reg(chip); } diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index b93b85dbc3a6..ba53b50d711a 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -51,7 +51,11 @@ struct isl29125_data { struct i2c_client *client; u8 conf1; - u16 buffer[8]; /* 3x 16-bit, padding, 8 bytes timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chans[3]; + s64 timestamp __aligned(8); + } scan; }; #define ISL29125_CHANNEL(_color, _si) { \ @@ -184,10 +188,10 @@ static irqreturn_t isl29125_trigger_handler(int irq, void *p) if (ret < 0) goto done; - data->buffer[j++] = ret; + data->scan.chans[j++] = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index bfade6577a38..a52b2c788540 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -186,9 +186,7 @@ static int pa12203001_set_power_state(struct pa12203001_data *data, bool on, } if (on) { - ret = pm_runtime_get_sync(&data->client->dev); - if (ret < 0) - pm_runtime_put_noidle(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); } else { pm_runtime_mark_last_busy(&data->client->dev); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 033578f444e4..c2dd8a3d4217 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -360,7 +360,7 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, * both stay enabled until _suspend(). */ 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); @@ -369,9 +369,6 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, dev_err(&data->client->dev, "Failed: rpr0521_set_power_state for %d, ret %d\n", on, ret); - if (on) - pm_runtime_put_noidle(&data->client->dev); - return ret; } @@ -985,7 +982,7 @@ static int rpr0521_probe(struct i2c_client *client, /* Trigger0 producer setup */ data->drdy_trigger0 = devm_iio_trigger_alloc( indio_dev->dev.parent, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!data->drdy_trigger0) { ret = -ENOMEM; goto err_pm_disable; @@ -1038,7 +1035,6 @@ static int rpr0521_probe(struct i2c_client *client, err_pm_disable: pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); err_poweroff: rpr0521_poweroff(data); @@ -1053,7 +1049,6 @@ static int rpr0521_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); rpr0521_poweroff(iio_priv(indio_dev)); diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 9b5c99823943..3fb52402fcc3 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1243,7 +1243,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) int ret; trig = devm_iio_trigger_alloc(&client->dev, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!trig) return -ENOMEM; diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 6fe5d46f80d4..0593abd600ec 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -53,7 +53,11 @@ struct tcs3414_data { u8 control; u8 gain; u8 timing; - u16 buffer[8]; /* 4x 16-bit + 8 bytes timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chans[4]; + s64 timestamp __aligned(8); + } scan; }; #define TCS3414_CHANNEL(_color, _si, _addr) { \ @@ -209,10 +213,10 @@ static irqreturn_t tcs3414_trigger_handler(int irq, void *p) if (ret < 0) goto done; - data->buffer[j++] = ret; + data->scan.chans[j++] = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index a0dc447aeb68..90dc3fef59e6 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -64,7 +64,11 @@ struct tcs3472_data { u8 control; u8 atime; u8 apers; - u16 buffer[8]; /* 4 16-bit channels + 64-bit timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chans[4]; + s64 timestamp __aligned(8); + } scan; }; static const struct iio_event_spec tcs3472_events[] = { @@ -386,10 +390,10 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) if (ret < 0) goto done; - data->buffer[j++] = ret; + data->scan.chans[j++] = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index c9d8f07a6fcd..7e101d5f72ee 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -644,9 +644,7 @@ static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(&chip->client->dev); - if (ret < 0) - pm_runtime_put_noidle(&chip->client->dev); + ret = pm_runtime_resume_and_get(&chip->client->dev); } else { pm_runtime_mark_last_busy(&chip->client->dev); ret = pm_runtime_put_autosuspend(&chip->client->dev); @@ -729,8 +727,10 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, read_done: mutex_unlock(&chip->als_mutex); - if (ret < 0) + if (ret < 0) { + tsl2583_set_pm_runtime_busy(chip, false); return ret; + } /* * Preserve the ret variable if the call to @@ -791,8 +791,10 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); - if (ret < 0) + if (ret < 0) { + tsl2583_set_pm_runtime_busy(chip, false); return ret; + } ret = tsl2583_set_pm_runtime_busy(chip, false); if (ret < 0) @@ -880,7 +882,6 @@ static int tsl2583_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF); } diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c new file mode 100644 index 000000000000..39e68d0c9d6a --- /dev/null +++ b/drivers/iio/light/tsl2591.c @@ -0,0 +1,1225 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Joe Sandom <joe.g.sandom@gmail.com> + * + * Datasheet: https://ams.com/tsl25911#tab/documents + * + * Device driver for the TAOS TSL2591. This is a very-high sensitivity + * light-to-digital converter that transforms light intensity into a digital + * signal. + */ + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/sysfs.h> + +#include <asm/unaligned.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* ADC integration time, field value to time in ms */ +#define TSL2591_FVAL_TO_MSEC(x) (((x) + 1) * 100) +/* ADC integration time, field value to time in seconds */ +#define TSL2591_FVAL_TO_SEC(x) ((x) + 1) +/* ADC integration time, time in seconds to field value */ +#define TSL2591_SEC_TO_FVAL(x) ((x) - 1) + +/* TSL2591 register set */ +#define TSL2591_ENABLE 0x00 +#define TSL2591_CONTROL 0x01 +#define TSL2591_AILTL 0x04 +#define TSL2591_AILTH 0x05 +#define TSL2591_AIHTL 0x06 +#define TSL2591_AIHTH 0x07 +#define TSL2591_NP_AILTL 0x08 +#define TSL2591_NP_AILTH 0x09 +#define TSL2591_NP_AIHTL 0x0A +#define TSL2591_NP_AIHTH 0x0B +#define TSL2591_PERSIST 0x0C +#define TSL2591_PACKAGE_ID 0x11 +#define TSL2591_DEVICE_ID 0x12 +#define TSL2591_STATUS 0x13 +#define TSL2591_C0_DATAL 0x14 +#define TSL2591_C0_DATAH 0x15 +#define TSL2591_C1_DATAL 0x16 +#define TSL2591_C1_DATAH 0x17 + +/* TSL2591 command register definitions */ +#define TSL2591_CMD_NOP 0xA0 +#define TSL2591_CMD_SF_INTSET 0xE4 +#define TSL2591_CMD_SF_CALS_I 0xE5 +#define TSL2591_CMD_SF_CALS_NPI 0xE7 +#define TSL2591_CMD_SF_CNP_ALSI 0xEA + +/* TSL2591 enable register definitions */ +#define TSL2591_PWR_ON 0x01 +#define TSL2591_PWR_OFF 0x00 +#define TSL2591_ENABLE_ALS 0x02 +#define TSL2591_ENABLE_ALS_INT 0x10 +#define TSL2591_ENABLE_SLEEP_INT 0x40 +#define TSL2591_ENABLE_NP_INT 0x80 + +/* TSL2591 control register definitions */ +#define TSL2591_CTRL_ALS_INTEGRATION_100MS 0x00 +#define TSL2591_CTRL_ALS_INTEGRATION_200MS 0x01 +#define TSL2591_CTRL_ALS_INTEGRATION_300MS 0x02 +#define TSL2591_CTRL_ALS_INTEGRATION_400MS 0x03 +#define TSL2591_CTRL_ALS_INTEGRATION_500MS 0x04 +#define TSL2591_CTRL_ALS_INTEGRATION_600MS 0x05 +#define TSL2591_CTRL_ALS_LOW_GAIN 0x00 +#define TSL2591_CTRL_ALS_MED_GAIN 0x10 +#define TSL2591_CTRL_ALS_HIGH_GAIN 0x20 +#define TSL2591_CTRL_ALS_MAX_GAIN 0x30 +#define TSL2591_CTRL_SYS_RESET 0x80 + +/* TSL2591 persist register definitions */ +#define TSL2591_PRST_ALS_INT_CYCLE_0 0x00 +#define TSL2591_PRST_ALS_INT_CYCLE_ANY 0x01 +#define TSL2591_PRST_ALS_INT_CYCLE_2 0x02 +#define TSL2591_PRST_ALS_INT_CYCLE_3 0x03 +#define TSL2591_PRST_ALS_INT_CYCLE_5 0x04 +#define TSL2591_PRST_ALS_INT_CYCLE_10 0x05 +#define TSL2591_PRST_ALS_INT_CYCLE_15 0x06 +#define TSL2591_PRST_ALS_INT_CYCLE_20 0x07 +#define TSL2591_PRST_ALS_INT_CYCLE_25 0x08 +#define TSL2591_PRST_ALS_INT_CYCLE_30 0x09 +#define TSL2591_PRST_ALS_INT_CYCLE_35 0x0A +#define TSL2591_PRST_ALS_INT_CYCLE_40 0x0B +#define TSL2591_PRST_ALS_INT_CYCLE_45 0x0C +#define TSL2591_PRST_ALS_INT_CYCLE_50 0x0D +#define TSL2591_PRST_ALS_INT_CYCLE_55 0x0E +#define TSL2591_PRST_ALS_INT_CYCLE_60 0x0F +#define TSL2591_PRST_ALS_INT_CYCLE_MAX (BIT(4) - 1) + +/* TSL2591 PID register mask */ +#define TSL2591_PACKAGE_ID_MASK GENMASK(5, 4) + +/* TSL2591 ID register mask */ +#define TSL2591_DEVICE_ID_MASK GENMASK(7, 0) + +/* TSL2591 status register masks */ +#define TSL2591_STS_ALS_VALID_MASK BIT(0) +#define TSL2591_STS_ALS_INT_MASK BIT(4) +#define TSL2591_STS_NPERS_INT_MASK BIT(5) +#define TSL2591_STS_VAL_HIGH_MASK BIT(0) + +/* TSL2591 constant values */ +#define TSL2591_PACKAGE_ID_VAL 0x00 +#define TSL2591_DEVICE_ID_VAL 0x50 + +/* Power off suspend delay time MS */ +#define TSL2591_POWER_OFF_DELAY_MS 2000 + +/* TSL2591 default values */ +#define TSL2591_DEFAULT_ALS_INT_TIME TSL2591_CTRL_ALS_INTEGRATION_300MS +#define TSL2591_DEFAULT_ALS_GAIN TSL2591_CTRL_ALS_MED_GAIN +#define TSL2591_DEFAULT_ALS_PERSIST TSL2591_PRST_ALS_INT_CYCLE_ANY +#define TSL2591_DEFAULT_ALS_LOWER_THRESH 100 +#define TSL2591_DEFAULT_ALS_UPPER_THRESH 1500 + +/* TSL2591 number of data registers */ +#define TSL2591_NUM_DATA_REGISTERS 4 + +/* TSL2591 number of valid status reads on ADC complete */ +#define TSL2591_ALS_STS_VALID_COUNT 10 + +/* TSL2591 delay period between polls when checking for ALS valid flag */ +#define TSL2591_DELAY_PERIOD_US 10000 + +/* TSL2591 maximum values */ +#define TSL2591_MAX_ALS_INT_TIME_MS 600 +#define TSL2591_ALS_MAX_VALUE (BIT(16) - 1) + +/* + * LUX calculations; + * AGAIN values from Adafruit's TSL2591 Arduino library + * https://github.com/adafruit/Adafruit_TSL2591_Library + */ +#define TSL2591_CTRL_ALS_LOW_GAIN_MULTIPLIER 1 +#define TSL2591_CTRL_ALS_MED_GAIN_MULTIPLIER 25 +#define TSL2591_CTRL_ALS_HIGH_GAIN_MULTIPLIER 428 +#define TSL2591_CTRL_ALS_MAX_GAIN_MULTIPLIER 9876 +#define TSL2591_LUX_COEFFICIENT 408 + +struct tsl2591_als_settings { + u16 als_lower_thresh; + u16 als_upper_thresh; + u8 als_int_time; + u8 als_persist; + u8 als_gain; +}; + +struct tsl2591_chip { + struct tsl2591_als_settings als_settings; + struct i2c_client *client; + /* + * Keep als_settings in sync with hardware state + * and ensure multiple readers are serialized. + */ + struct mutex als_mutex; + bool events_enabled; +}; + +/* + * Period table is ALS persist cycle x integration time setting + * Integration times: 100ms, 200ms, 300ms, 400ms, 500ms, 600ms + * ALS cycles: 1, 2, 3, 5, 10, 20, 25, 30, 35, 40, 45, 50, 55, 60 + */ +static const char * const tsl2591_als_period_list[] = { + "0.1 0.2 0.3 0.5 1.0 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0", + "0.2 0.4 0.6 1.0 2.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0", + "0.3 0.6 0.9 1.5 3.0 6.0 7.5 9.0 10.5 12.0 13.5 15.0 16.5 18.0", + "0.4 0.8 1.2 2.0 4.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0 22.0 24.0", + "0.5 1.0 1.5 2.5 5.0 10.0 12.5 15.0 17.5 20.0 22.5 25.0 27.5 30.0", + "0.6 1.2 1.8 3.0 6.0 12.0 15.0 18.0 21.0 24.0 27.0 30.0 33.0 36.0", +}; + +static const int tsl2591_int_time_available[] = { + 1, 2, 3, 4, 5, 6, +}; + +static const int tsl2591_calibscale_available[] = { + 1, 25, 428, 9876, +}; + +static int tsl2591_set_als_lower_threshold(struct tsl2591_chip *chip, + u16 als_lower_threshold); +static int tsl2591_set_als_upper_threshold(struct tsl2591_chip *chip, + u16 als_upper_threshold); + +static int tsl2591_gain_to_multiplier(const u8 als_gain) +{ + switch (als_gain) { + case TSL2591_CTRL_ALS_LOW_GAIN: + return TSL2591_CTRL_ALS_LOW_GAIN_MULTIPLIER; + case TSL2591_CTRL_ALS_MED_GAIN: + return TSL2591_CTRL_ALS_MED_GAIN_MULTIPLIER; + case TSL2591_CTRL_ALS_HIGH_GAIN: + return TSL2591_CTRL_ALS_HIGH_GAIN_MULTIPLIER; + case TSL2591_CTRL_ALS_MAX_GAIN: + return TSL2591_CTRL_ALS_MAX_GAIN_MULTIPLIER; + default: + return -EINVAL; + } +} + +static int tsl2591_multiplier_to_gain(const u32 multiplier) +{ + switch (multiplier) { + case TSL2591_CTRL_ALS_LOW_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_LOW_GAIN; + case TSL2591_CTRL_ALS_MED_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_MED_GAIN; + case TSL2591_CTRL_ALS_HIGH_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_HIGH_GAIN; + case TSL2591_CTRL_ALS_MAX_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_MAX_GAIN; + default: + return -EINVAL; + } +} + +static int tsl2591_persist_cycle_to_lit(const u8 als_persist) +{ + switch (als_persist) { + case TSL2591_PRST_ALS_INT_CYCLE_ANY: + return 1; + case TSL2591_PRST_ALS_INT_CYCLE_2: + return 2; + case TSL2591_PRST_ALS_INT_CYCLE_3: + return 3; + case TSL2591_PRST_ALS_INT_CYCLE_5: + return 5; + case TSL2591_PRST_ALS_INT_CYCLE_10: + return 10; + case TSL2591_PRST_ALS_INT_CYCLE_15: + return 15; + case TSL2591_PRST_ALS_INT_CYCLE_20: + return 20; + case TSL2591_PRST_ALS_INT_CYCLE_25: + return 25; + case TSL2591_PRST_ALS_INT_CYCLE_30: + return 30; + case TSL2591_PRST_ALS_INT_CYCLE_35: + return 35; + case TSL2591_PRST_ALS_INT_CYCLE_40: + return 40; + case TSL2591_PRST_ALS_INT_CYCLE_45: + return 45; + case TSL2591_PRST_ALS_INT_CYCLE_50: + return 50; + case TSL2591_PRST_ALS_INT_CYCLE_55: + return 55; + case TSL2591_PRST_ALS_INT_CYCLE_60: + return 60; + default: + return -EINVAL; + } +} + +static int tsl2591_persist_lit_to_cycle(const u8 als_persist) +{ + switch (als_persist) { + case 1: + return TSL2591_PRST_ALS_INT_CYCLE_ANY; + case 2: + return TSL2591_PRST_ALS_INT_CYCLE_2; + case 3: + return TSL2591_PRST_ALS_INT_CYCLE_3; + case 5: + return TSL2591_PRST_ALS_INT_CYCLE_5; + case 10: + return TSL2591_PRST_ALS_INT_CYCLE_10; + case 15: + return TSL2591_PRST_ALS_INT_CYCLE_15; + case 20: + return TSL2591_PRST_ALS_INT_CYCLE_20; + case 25: + return TSL2591_PRST_ALS_INT_CYCLE_25; + case 30: + return TSL2591_PRST_ALS_INT_CYCLE_30; + case 35: + return TSL2591_PRST_ALS_INT_CYCLE_35; + case 40: + return TSL2591_PRST_ALS_INT_CYCLE_40; + case 45: + return TSL2591_PRST_ALS_INT_CYCLE_45; + case 50: + return TSL2591_PRST_ALS_INT_CYCLE_50; + case 55: + return TSL2591_PRST_ALS_INT_CYCLE_55; + case 60: + return TSL2591_PRST_ALS_INT_CYCLE_60; + default: + return -EINVAL; + } +} + +static int tsl2591_compatible_int_time(struct tsl2591_chip *chip, + const u32 als_integration_time) +{ + switch (als_integration_time) { + case TSL2591_CTRL_ALS_INTEGRATION_100MS: + case TSL2591_CTRL_ALS_INTEGRATION_200MS: + case TSL2591_CTRL_ALS_INTEGRATION_300MS: + case TSL2591_CTRL_ALS_INTEGRATION_400MS: + case TSL2591_CTRL_ALS_INTEGRATION_500MS: + case TSL2591_CTRL_ALS_INTEGRATION_600MS: + return 0; + default: + return -EINVAL; + } +} + +static int tsl2591_als_time_to_fval(const u32 als_integration_time) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tsl2591_int_time_available); i++) { + if (als_integration_time == tsl2591_int_time_available[i]) + return TSL2591_SEC_TO_FVAL(als_integration_time); + } + + return -EINVAL; +} + +static int tsl2591_compatible_gain(struct tsl2591_chip *chip, const u8 als_gain) +{ + switch (als_gain) { + case TSL2591_CTRL_ALS_LOW_GAIN: + case TSL2591_CTRL_ALS_MED_GAIN: + case TSL2591_CTRL_ALS_HIGH_GAIN: + case TSL2591_CTRL_ALS_MAX_GAIN: + return 0; + default: + return -EINVAL; + } +} + +static int tsl2591_compatible_als_persist_cycle(struct tsl2591_chip *chip, + const u32 als_persist) +{ + switch (als_persist) { + case TSL2591_PRST_ALS_INT_CYCLE_ANY: + case TSL2591_PRST_ALS_INT_CYCLE_2: + case TSL2591_PRST_ALS_INT_CYCLE_3: + case TSL2591_PRST_ALS_INT_CYCLE_5: + case TSL2591_PRST_ALS_INT_CYCLE_10: + case TSL2591_PRST_ALS_INT_CYCLE_15: + case TSL2591_PRST_ALS_INT_CYCLE_20: + case TSL2591_PRST_ALS_INT_CYCLE_25: + case TSL2591_PRST_ALS_INT_CYCLE_30: + case TSL2591_PRST_ALS_INT_CYCLE_35: + case TSL2591_PRST_ALS_INT_CYCLE_40: + case TSL2591_PRST_ALS_INT_CYCLE_45: + case TSL2591_PRST_ALS_INT_CYCLE_50: + case TSL2591_PRST_ALS_INT_CYCLE_55: + case TSL2591_PRST_ALS_INT_CYCLE_60: + return 0; + default: + return -EINVAL; + } +} + +static int tsl2591_check_als_valid(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, TSL2591_CMD_NOP | TSL2591_STATUS); + if (ret < 0) { + dev_err(&client->dev, "Failed to read register\n"); + return -EINVAL; + } + + return FIELD_GET(TSL2591_STS_ALS_VALID_MASK, ret); +} + +static int tsl2591_wait_adc_complete(struct tsl2591_chip *chip) +{ + struct tsl2591_als_settings settings = chip->als_settings; + struct i2c_client *client = chip->client; + int delay; + int val; + int ret; + + delay = TSL2591_FVAL_TO_MSEC(settings.als_int_time); + if (!delay) + return -EINVAL; + + /* + * Sleep for ALS integration time to allow enough time or an ADC read + * cycle to complete. Check status after delay for ALS valid. + */ + msleep(delay); + + /* Check for status ALS valid flag for up to 100ms */ + ret = readx_poll_timeout(tsl2591_check_als_valid, client, + val, val == TSL2591_STS_VAL_HIGH_MASK, + TSL2591_DELAY_PERIOD_US, + TSL2591_DELAY_PERIOD_US * TSL2591_ALS_STS_VALID_COUNT); + if (ret) + dev_err(&client->dev, "Timed out waiting for valid ALS data\n"); + + return ret; +} + +/* + * tsl2591_read_channel_data - Reads raw channel data and calculates lux + * + * Formula for lux calculation; + * Derived from Adafruit's TSL2591 library + * Link: https://github.com/adafruit/Adafruit_TSL2591_Library + * Counts Per Lux (CPL) = (ATIME_ms * AGAIN) / LUX DF + * lux = ((C0DATA - C1DATA) * (1 - (C1DATA / C0DATA))) / CPL + * + * Scale values to get more representative value of lux i.e. + * lux = ((C0DATA - C1DATA) * (1000 - ((C1DATA * 1000) / C0DATA))) / CPL + * + * Channel 0 = IR + Visible + * Channel 1 = IR only + */ +static int tsl2591_read_channel_data(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct tsl2591_als_settings *settings = &chip->als_settings; + struct i2c_client *client = chip->client; + u8 als_data[TSL2591_NUM_DATA_REGISTERS]; + int counts_per_lux, int_time_fval, gain_multi, lux; + u16 als_ch0, als_ch1; + int ret; + + ret = tsl2591_wait_adc_complete(chip); + if (ret < 0) { + dev_err(&client->dev, "No data available. Err: %d\n", ret); + return ret; + } + + ret = i2c_smbus_read_i2c_block_data(client, + TSL2591_CMD_NOP | TSL2591_C0_DATAL, + sizeof(als_data), als_data); + if (ret < 0) { + dev_err(&client->dev, "Failed to read data bytes"); + return ret; + } + + als_ch0 = get_unaligned_le16(&als_data[0]); + als_ch1 = get_unaligned_le16(&als_data[2]); + + switch (chan->type) { + case IIO_INTENSITY: + if (chan->channel2 == IIO_MOD_LIGHT_BOTH) + *val = als_ch0; + else if (chan->channel2 == IIO_MOD_LIGHT_IR) + *val = als_ch1; + else + return -EINVAL; + break; + case IIO_LIGHT: + gain_multi = tsl2591_gain_to_multiplier(settings->als_gain); + if (gain_multi < 0) { + dev_err(&client->dev, "Invalid multiplier"); + return gain_multi; + } + + int_time_fval = TSL2591_FVAL_TO_MSEC(settings->als_int_time); + /* Calculate counts per lux value */ + counts_per_lux = (int_time_fval * gain_multi) / TSL2591_LUX_COEFFICIENT; + + dev_dbg(&client->dev, "Counts Per Lux: %d\n", counts_per_lux); + + /* Calculate lux value */ + lux = ((als_ch0 - als_ch1) * + (1000 - ((als_ch1 * 1000) / als_ch0))) / counts_per_lux; + + dev_dbg(&client->dev, "Raw lux calculation: %d\n", lux); + + /* Divide by 1000 to get real lux value before scaling */ + *val = lux / 1000; + + /* Get the decimal part of lux reading */ + *val2 = (lux - (*val * 1000)) * 1000; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tsl2591_set_als_gain_int_time(struct tsl2591_chip *chip) +{ + struct tsl2591_als_settings als_settings = chip->als_settings; + struct i2c_client *client = chip->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_CONTROL, + als_settings.als_int_time | als_settings.als_gain); + if (ret) + dev_err(&client->dev, "Failed to set als gain & int time\n"); + + return ret; +} + +static int tsl2591_set_als_lower_threshold(struct tsl2591_chip *chip, + u16 als_lower_threshold) +{ + struct tsl2591_als_settings als_settings = chip->als_settings; + struct i2c_client *client = chip->client; + u16 als_upper_threshold; + u8 als_lower_l; + u8 als_lower_h; + int ret; + + chip->als_settings.als_lower_thresh = als_lower_threshold; + + /* + * Lower threshold should not be greater or equal to upper. + * If this is the case, then assert upper threshold to new lower + * threshold + 1 to avoid ordering issues when setting thresholds. + */ + if (als_lower_threshold >= als_settings.als_upper_thresh) { + als_upper_threshold = als_lower_threshold + 1; + tsl2591_set_als_upper_threshold(chip, als_upper_threshold); + } + + als_lower_l = als_lower_threshold; + als_lower_h = als_lower_threshold >> 8; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AILTL, + als_lower_l); + if (ret) { + dev_err(&client->dev, "Failed to set als lower threshold\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AILTH, + als_lower_h); + if (ret) { + dev_err(&client->dev, "Failed to set als lower threshold\n"); + return ret; + } + + return 0; +} + +static int tsl2591_set_als_upper_threshold(struct tsl2591_chip *chip, + u16 als_upper_threshold) +{ + struct tsl2591_als_settings als_settings = chip->als_settings; + struct i2c_client *client = chip->client; + u16 als_lower_threshold; + u8 als_upper_l; + u8 als_upper_h; + int ret; + + if (als_upper_threshold > TSL2591_ALS_MAX_VALUE) + return -EINVAL; + + chip->als_settings.als_upper_thresh = als_upper_threshold; + + /* + * Upper threshold should not be less than lower. If this + * is the case, then assert lower threshold to new upper + * threshold - 1 to avoid ordering issues when setting thresholds. + */ + if (als_upper_threshold < als_settings.als_lower_thresh) { + als_lower_threshold = als_upper_threshold - 1; + tsl2591_set_als_lower_threshold(chip, als_lower_threshold); + } + + als_upper_l = als_upper_threshold; + als_upper_h = als_upper_threshold >> 8; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AIHTL, + als_upper_l); + if (ret) { + dev_err(&client->dev, "Failed to set als upper threshold\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AIHTH, + als_upper_h); + if (ret) { + dev_err(&client->dev, "Failed to set als upper threshold\n"); + return ret; + } + + return 0; +} + +static int tsl2591_set_als_persist_cycle(struct tsl2591_chip *chip, + u8 als_persist) +{ + struct i2c_client *client = chip->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_PERSIST, + als_persist); + if (ret) + dev_err(&client->dev, "Failed to set als persist cycle\n"); + + chip->als_settings.als_persist = als_persist; + + return ret; +} + +static int tsl2591_set_power_state(struct tsl2591_chip *chip, u8 state) +{ + struct i2c_client *client = chip->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_ENABLE, + state); + if (ret) + dev_err(&client->dev, + "Failed to set the power state to %#04x\n", state); + + return ret; +} + +static ssize_t tsl2591_in_illuminance_period_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2591_chip *chip = iio_priv(indio_dev); + + return sysfs_emit(buf, "%s\n", + tsl2591_als_period_list[chip->als_settings.als_int_time]); +} + +static IIO_DEVICE_ATTR_RO(tsl2591_in_illuminance_period_available, 0); + +static struct attribute *tsl2591_event_attrs_ctrl[] = { + &iio_dev_attr_tsl2591_in_illuminance_period_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group tsl2591_event_attribute_group = { + .attrs = tsl2591_event_attrs_ctrl, +}; + +static const struct iio_event_spec tsl2591_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec tsl2591_channels[] = { + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_IR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BOTH, + .event_spec = tsl2591_events, + .num_event_specs = ARRAY_SIZE(tsl2591_events), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) + }, +}; + +static int tsl2591_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + int ret; + + pm_runtime_get_sync(&client->dev); + + mutex_lock(&chip->als_mutex); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_INTENSITY) { + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_read_channel_data(indio_dev, chan, val, val2); + if (ret < 0) + goto err_unlock; + + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_LIGHT) { + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_read_channel_data(indio_dev, chan, val, val2); + if (ret < 0) + break; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_INT_TIME: + if (chan->type != IIO_INTENSITY) { + ret = -EINVAL; + goto err_unlock; + } + + *val = TSL2591_FVAL_TO_SEC(chip->als_settings.als_int_time); + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type != IIO_INTENSITY) { + ret = -EINVAL; + goto err_unlock; + } + + *val = tsl2591_gain_to_multiplier(chip->als_settings.als_gain); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + mutex_unlock(&chip->als_mutex); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static int tsl2591_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + int int_time; + int gain; + int ret; + + mutex_lock(&chip->als_mutex); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + int_time = tsl2591_als_time_to_fval(val); + if (int_time < 0) { + ret = int_time; + goto err_unlock; + } + ret = tsl2591_compatible_int_time(chip, int_time); + if (ret < 0) + goto err_unlock; + + chip->als_settings.als_int_time = int_time; + break; + case IIO_CHAN_INFO_CALIBSCALE: + gain = tsl2591_multiplier_to_gain(val); + if (gain < 0) { + ret = gain; + goto err_unlock; + } + ret = tsl2591_compatible_gain(chip, gain); + if (ret < 0) + goto err_unlock; + + chip->als_settings.als_gain = gain; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_set_als_gain_int_time(chip); + +err_unlock: + mutex_unlock(&chip->als_mutex); + return ret; +} + +static int tsl2591_read_available(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_INT_TIME: + *length = ARRAY_SIZE(tsl2591_int_time_available); + *vals = tsl2591_int_time_available; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_CALIBSCALE: + *length = ARRAY_SIZE(tsl2591_calibscale_available); + *vals = tsl2591_calibscale_available; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int tsl2591_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + int als_persist, int_time, period; + int ret; + + mutex_lock(&chip->als_mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = chip->als_settings.als_upper_thresh; + break; + case IIO_EV_DIR_FALLING: + *val = chip->als_settings.als_lower_thresh; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + ret = IIO_VAL_INT; + break; + case IIO_EV_INFO_PERIOD: + ret = i2c_smbus_read_byte_data(client, + TSL2591_CMD_NOP | TSL2591_PERSIST); + if (ret <= 0 || ret > TSL2591_PRST_ALS_INT_CYCLE_MAX) + goto err_unlock; + + als_persist = tsl2591_persist_cycle_to_lit(ret); + int_time = TSL2591_FVAL_TO_MSEC(chip->als_settings.als_int_time); + period = als_persist * (int_time * MSEC_PER_SEC); + + *val = period / USEC_PER_SEC; + *val2 = period % USEC_PER_SEC; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + mutex_unlock(&chip->als_mutex); + return ret; +} + +static int tsl2591_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, + int val2) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + int period, int_time, als_persist; + int ret; + + if (val < 0 || val2 < 0) + return -EINVAL; + + mutex_lock(&chip->als_mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val > TSL2591_ALS_MAX_VALUE) { + ret = -EINVAL; + goto err_unlock; + } + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = tsl2591_set_als_upper_threshold(chip, val); + if (ret < 0) + goto err_unlock; + break; + case IIO_EV_DIR_FALLING: + ret = tsl2591_set_als_lower_threshold(chip, val); + if (ret < 0) + goto err_unlock; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + break; + case IIO_EV_INFO_PERIOD: + int_time = TSL2591_FVAL_TO_MSEC(chip->als_settings.als_int_time); + + period = ((val * MSEC_PER_SEC) + + (val2 / MSEC_PER_SEC)) / int_time; + + als_persist = tsl2591_persist_lit_to_cycle(period); + if (als_persist < 0) { + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_compatible_als_persist_cycle(chip, als_persist); + if (ret < 0) + goto err_unlock; + + ret = tsl2591_set_als_persist_cycle(chip, als_persist); + if (ret < 0) + goto err_unlock; + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + mutex_unlock(&chip->als_mutex); + return ret; +} + +static int tsl2591_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + + return chip->events_enabled; +} + +static int tsl2591_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + + if (state && !chip->events_enabled) { + chip->events_enabled = true; + pm_runtime_get_sync(&client->dev); + } else if (!state && chip->events_enabled) { + chip->events_enabled = false; + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + } + + return 0; +} + +static const struct iio_info tsl2591_info = { + .event_attrs = &tsl2591_event_attribute_group, + .read_raw = tsl2591_read_raw, + .write_raw = tsl2591_write_raw, + .read_avail = tsl2591_read_available, + .read_event_value = tsl2591_read_event_value, + .write_event_value = tsl2591_write_event_value, + .read_event_config = tsl2591_read_event_config, + .write_event_config = tsl2591_write_event_config, +}; + +static const struct iio_info tsl2591_info_no_irq = { + .read_raw = tsl2591_read_raw, + .write_raw = tsl2591_write_raw, + .read_avail = tsl2591_read_available, +}; + +static int __maybe_unused tsl2591_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2591_chip *chip = iio_priv(indio_dev); + int ret; + + mutex_lock(&chip->als_mutex); + ret = tsl2591_set_power_state(chip, TSL2591_PWR_OFF); + mutex_unlock(&chip->als_mutex); + + return ret; +} + +static int __maybe_unused tsl2591_resume(struct device *dev) +{ + int power_state = TSL2591_PWR_ON | TSL2591_ENABLE_ALS; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2591_chip *chip = iio_priv(indio_dev); + int ret; + + if (chip->events_enabled) + power_state |= TSL2591_ENABLE_ALS_INT; + + mutex_lock(&chip->als_mutex); + ret = tsl2591_set_power_state(chip, power_state); + mutex_unlock(&chip->als_mutex); + + return ret; +} + +static const struct dev_pm_ops tsl2591_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(tsl2591_suspend, tsl2591_resume, NULL) +}; + +static irqreturn_t tsl2591_event_handler(int irq, void *private) +{ + struct iio_dev *dev_info = private; + struct tsl2591_chip *chip = iio_priv(dev_info); + struct i2c_client *client = chip->client; + + if (!chip->events_enabled) + return IRQ_NONE; + + iio_push_event(dev_info, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(dev_info)); + + /* Clear ALS irq */ + i2c_smbus_write_byte(client, TSL2591_CMD_SF_CALS_NPI); + + return IRQ_HANDLED; +} + +static int tsl2591_load_defaults(struct tsl2591_chip *chip) +{ + int ret; + + chip->als_settings.als_int_time = TSL2591_DEFAULT_ALS_INT_TIME; + chip->als_settings.als_gain = TSL2591_DEFAULT_ALS_GAIN; + chip->als_settings.als_lower_thresh = TSL2591_DEFAULT_ALS_LOWER_THRESH; + chip->als_settings.als_upper_thresh = TSL2591_DEFAULT_ALS_UPPER_THRESH; + + ret = tsl2591_set_als_gain_int_time(chip); + if (ret < 0) + return ret; + + ret = tsl2591_set_als_persist_cycle(chip, TSL2591_DEFAULT_ALS_PERSIST); + if (ret < 0) + return ret; + + ret = tsl2591_set_als_lower_threshold(chip, TSL2591_DEFAULT_ALS_LOWER_THRESH); + if (ret < 0) + return ret; + + ret = tsl2591_set_als_upper_threshold(chip, TSL2591_DEFAULT_ALS_UPPER_THRESH); + if (ret < 0) + return ret; + + return 0; +} + +static void tsl2591_chip_off(void *data) +{ + struct iio_dev *indio_dev = data; + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + tsl2591_set_power_state(chip, TSL2591_PWR_OFF); +} + +static int tsl2591_probe(struct i2c_client *client) +{ + struct tsl2591_chip *chip; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "I2C smbus byte data functionality is not supported\n"); + return -EOPNOTSUPP; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + chip->client = client; + i2c_set_clientdata(client, indio_dev); + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, tsl2591_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tsl2591_irq", indio_dev); + if (ret) { + dev_err_probe(&client->dev, ret, "IRQ request error\n"); + return -EINVAL; + } + indio_dev->info = &tsl2591_info; + } else { + indio_dev->info = &tsl2591_info_no_irq; + } + + mutex_init(&chip->als_mutex); + + ret = i2c_smbus_read_byte_data(client, + TSL2591_CMD_NOP | TSL2591_DEVICE_ID); + if (ret < 0) { + dev_err(&client->dev, + "Failed to read the device ID register\n"); + return ret; + } + ret = FIELD_GET(TSL2591_DEVICE_ID_MASK, ret); + if (ret != TSL2591_DEVICE_ID_VAL) { + dev_err(&client->dev, "Device ID: %#04x unknown\n", ret); + return -EINVAL; + } + + indio_dev->channels = tsl2591_channels; + indio_dev->num_channels = ARRAY_SIZE(tsl2591_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = chip->client->name; + chip->events_enabled = false; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, + TSL2591_POWER_OFF_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + /* + * Add chip off to automatically managed path and disable runtime + * power management. This ensures that the chip power management + * is handled correctly on driver remove. tsl2591_chip_off() must be + * added to the managed path after pm runtime is enabled and before + * any error exit paths are met to ensure we're not left in a state + * of pm runtime not being disabled properly. + */ + ret = devm_add_action_or_reset(&client->dev, tsl2591_chip_off, + indio_dev); + if (ret < 0) + return -EINVAL; + + ret = tsl2591_load_defaults(chip); + if (ret < 0) { + dev_err(&client->dev, "Failed to load sensor defaults\n"); + return -EINVAL; + } + + ret = i2c_smbus_write_byte(client, TSL2591_CMD_SF_CALS_NPI); + if (ret < 0) { + dev_err(&client->dev, "Failed to clear als irq\n"); + return -EINVAL; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id tsl2591_of_match[] = { + { .compatible = "amstaos,tsl2591"}, + {} +}; +MODULE_DEVICE_TABLE(of, tsl2591_of_match); + +static struct i2c_driver tsl2591_driver = { + .driver = { + .name = "tsl2591", + .pm = &tsl2591_pm_ops, + .of_match_table = tsl2591_of_match, + }, + .probe_new = tsl2591_probe +}; +module_i2c_driver(tsl2591_driver); + +MODULE_AUTHOR("Joe Sandom <joe.g.sandom@gmail.com>"); +MODULE_DESCRIPTION("TAOS tsl2591 ambient light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index 393f27b75c75..96e4a66ddf28 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -367,9 +367,7 @@ static int us5182d_set_power_state(struct us5182d_data *data, bool on) return 0; if (on) { - ret = pm_runtime_get_sync(&data->client->dev); - if (ret < 0) - pm_runtime_put_noidle(&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); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 2f7916f95689..01772327a947 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -413,9 +413,7 @@ static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -998,7 +996,8 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) struct iio_trigger *trigger; trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!trigger) return -ENOMEM; diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index ae87740d9cef..fd2f181b16db 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -144,9 +144,7 @@ static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) struct device *dev = &data->client->dev; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -507,7 +505,7 @@ static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) data->drdy_trigger0 = devm_iio_trigger_alloc( indio_dev->dev.parent, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!data->drdy_trigger0) return -ENOMEM; diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index de85c9b30be1..3c937c55a10d 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -128,7 +128,7 @@ static ssize_t in_illuminance_period_available_show(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE, "%s\n", period_values[x]); + return sysfs_emit(buf, "%s\n", period_values[x]); } static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0); diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 24b2f7b1fe44..e54feacfb980 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -833,8 +833,7 @@ static int ak8974_probe(struct i2c_client *i2c, ak8974->i2c = i2c; mutex_init(&ak8974->lock); - ret = iio_read_mount_matrix(&i2c->dev, "mount-matrix", - &ak8974->orientation); + ret = iio_read_mount_matrix(&i2c->dev, &ak8974->orientation); if (ret) return ret; diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index d988b6ac3659..42b8a2680e3a 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -890,7 +890,7 @@ static int ak8975_probe(struct i2c_client *client, data->reset_gpiod = reset_gpiod; data->eoc_irq = 0; - err = iio_read_mount_matrix(&client->dev, "mount-matrix", &data->orientation); + err = iio_read_mount_matrix(&client->dev, &data->orientation); if (err) return err; diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 00f9766bad5c..f96f53175349 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -138,8 +138,11 @@ struct bmc150_magn_data { struct regmap *regmap; struct regulator_bulk_data regulators[2]; struct iio_mount_matrix orientation; - /* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */ - s32 buffer[6]; + /* Ensure timestamp is naturally aligned */ + struct { + s32 chans[3]; + s64 timestamp __aligned(8); + } scan; struct iio_trigger *dready_trig; bool dready_trigger_on; int max_odr; @@ -262,7 +265,7 @@ static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); } else { pm_runtime_mark_last_busy(data->dev); ret = pm_runtime_put_autosuspend(data->dev); @@ -271,9 +274,6 @@ static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) if (ret < 0) { dev_err(data->dev, "failed to change power state to %d\n", on); - if (on) - pm_runtime_put_noidle(data->dev); - return ret; } #endif @@ -675,11 +675,11 @@ static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p) int ret; mutex_lock(&data->mutex); - ret = bmc150_magn_read_xyz(data, data->buffer); + ret = bmc150_magn_read_xyz(data, data->scan.chans); 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: @@ -890,8 +890,7 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, if (ret) return dev_err_probe(dev, ret, "failed to get regulators\n"); - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; @@ -915,7 +914,7 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; dev_err(dev, "iio trigger alloc failed\n"); @@ -963,12 +962,14 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(dev, "unable to register iio device\n"); - goto err_buffer_cleanup; + goto err_disable_runtime_pm; } dev_dbg(dev, "Registered device %s\n", name); return 0; +err_disable_runtime_pm: + pm_runtime_disable(dev); err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); err_free_irq: @@ -992,7 +993,6 @@ int bmc150_magn_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); iio_triggered_buffer_cleanup(indio_dev); diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 3f6c0b662941..242f742f2643 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -33,7 +33,8 @@ enum hmc5843_ids { * @lock: update and read regmap data * @regmap: hardware access register maps * @variant: describe chip variants - * @buffer: 3x 16-bit channels + padding + 64-bit timestamp + * @scan: buffer to pack data for passing to + * iio_push_to_buffers_with_timestamp() */ struct hmc5843_data { struct device *dev; @@ -41,7 +42,10 @@ struct hmc5843_data { struct regmap *regmap; const struct hmc5843_chip_info *variant; struct iio_mount_matrix orientation; - __be16 buffer[8]; + struct { + __be16 chans[3]; + s64 timestamp __aligned(8); + } scan; }; int hmc5843_common_probe(struct device *dev, struct regmap *regmap, diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 780faea61d82..cf62057480cf 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -446,13 +446,13 @@ static irqreturn_t hmc5843_trigger_handler(int irq, void *p) } ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS, - data->buffer, 3 * sizeof(__be16)); + data->scan.chans, sizeof(data->scan.chans)); mutex_unlock(&data->lock); if (ret < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: @@ -637,8 +637,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap, data->variant = &hmc5843_chip_info_tbl[id]; mutex_init(&data->lock); - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index dd811da9cb6d..4df5887fd04c 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -575,7 +575,7 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->drdy_trig) return -ENOMEM; diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 7ba6a6ba5c58..fb6c906c4c0c 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -23,10 +23,6 @@ #define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" #define IIS2MDC_MAGN_DEV_NAME "iis2mdc" -const struct st_sensor_settings *st_magn_get_settings(const char *name); -int st_magn_common_probe(struct iio_dev *indio_dev); -void st_magn_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_magn_allocate_ring(struct iio_dev *indio_dev); void st_magn_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 71faebd07feb..0048c3cd36ee 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -33,6 +33,7 @@ /* FULLSCALE */ #define ST_MAGN_FS_AVL_1300MG 1300 #define ST_MAGN_FS_AVL_1900MG 1900 +#define ST_MAGN_FS_AVL_2000MG 2000 #define ST_MAGN_FS_AVL_2500MG 2500 #define ST_MAGN_FS_AVL_4000MG 4000 #define ST_MAGN_FS_AVL_4700MG 4700 @@ -53,51 +54,95 @@ #define ST_MAGN_3_OUT_Y_L_ADDR 0x6a #define ST_MAGN_3_OUT_Z_L_ADDR 0x6c +/* Special L addresses for sensor 4 */ +#define ST_MAGN_4_OUT_X_L_ADDR 0x08 +#define ST_MAGN_4_OUT_Y_L_ADDR 0x0a +#define ST_MAGN_4_OUT_Z_L_ADDR 0x0c + +static const struct iio_mount_matrix * +st_magn_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *mdata = iio_priv(indio_dev); + + return &mdata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info st_magn_mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_magn_get_mount_matrix), + { } +}; + static const struct iio_chan_spec st_magn_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_BE, 16, 16, - ST_MAGN_DEFAULT_OUT_X_H_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_DEFAULT_OUT_X_H_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_BE, 16, 16, - ST_MAGN_DEFAULT_OUT_Y_H_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_DEFAULT_OUT_Y_H_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_BE, 16, 16, - ST_MAGN_DEFAULT_OUT_Z_H_ADDR), + ST_MAGN_DEFAULT_OUT_Z_H_ADDR, + st_magn_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_magn_2_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_MAGN_2_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_2_OUT_X_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_MAGN_2_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_2_OUT_Y_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_MAGN_2_OUT_Z_L_ADDR), + ST_MAGN_2_OUT_Z_L_ADDR, + st_magn_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_magn_3_16bit_channels[] = { + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, + ST_MAGN_3_OUT_X_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, + ST_MAGN_3_OUT_Y_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, + ST_MAGN_3_OUT_Z_L_ADDR, + st_magn_mount_matrix_ext_info), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct iio_chan_spec st_magn_4_16bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_MAGN_3_OUT_X_L_ADDR), + ST_MAGN_4_OUT_X_L_ADDR), ST_SENSORS_LSM_CHANNELS(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_MAGN_3_OUT_Y_L_ADDR), + ST_MAGN_4_OUT_Y_L_ADDR), ST_SENSORS_LSM_CHANNELS(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_MAGN_3_OUT_Z_L_ADDR), + ST_MAGN_4_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -381,6 +426,87 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .multi_read_bit = false, .bootime = 2, }, + { + .wai = 0x49, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LSM9DS0_IMU_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_magn_4_16bit_channels, + .odr = { + .addr = 0x24, + .mask = GENMASK(4, 2), + .odr_avl = { + { 3, 0x00, }, + { 6, 0x01, }, + { 12, 0x02, }, + { 25, 0x03, }, + { 50, 0x04, }, + { 100, 0x05, }, + }, + }, + .pw = { + .addr = 0x26, + .mask = GENMASK(1, 0), + .value_on = 0x00, + .value_off = 0x03, + }, + .fs = { + .addr = 0x25, + .mask = GENMASK(6, 5), + .fs_avl = { + [0] = { + .num = ST_MAGN_FS_AVL_2000MG, + .value = 0x00, + .gain = 73, + }, + [1] = { + .num = ST_MAGN_FS_AVL_4000MG, + .value = 0x01, + .gain = 146, + }, + [2] = { + .num = ST_MAGN_FS_AVL_8000MG, + .value = 0x02, + .gain = 292, + }, + [3] = { + .num = ST_MAGN_FS_AVL_12000MG, + .value = 0x03, + .gain = 438, + }, + }, + }, + .bdu = { + .addr = 0x20, + .mask = BIT(3), + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = BIT(1), + }, + .int2 = { + .addr = 0x23, + .mask = BIT(2), + }, + .stat_drdy = { + .addr = 0x07, + .mask = GENMASK(2, 0), + }, + }, + .sim = { + .addr = 0x21, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, +}; + +/* Default magn DRDY is available on INT2 pin */ +static const struct st_sensors_platform_data default_magn_pdata = { + .drdy_int_pin = 2, }; static int st_magn_read_raw(struct iio_dev *indio_dev, @@ -490,33 +616,37 @@ EXPORT_SYMBOL(st_magn_get_settings); int st_magn_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); + struct st_sensors_platform_data *pdata = dev_get_platdata(mdata->dev); int err; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &magn_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_magn_power_off; + return err; mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS; indio_dev->channels = mdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + err = iio_read_mount_matrix(mdata->dev, &mdata->mount_matrix); + if (err) + return err; + mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0]; mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz; - err = st_sensors_init_sensor(indio_dev, NULL); + if (!pdata) + pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + + err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_magn_power_off; + return err; err = st_magn_allocate_ring(indio_dev); if (err < 0) - goto st_magn_power_off; + return err; if (mdata->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -539,9 +669,6 @@ st_magn_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_magn_probe_trigger_error: st_magn_deallocate_ring(indio_dev); -st_magn_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_magn_common_probe); @@ -550,8 +677,6 @@ void st_magn_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (mdata->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 36f4e7b53b24..3e23c117de8e 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -82,16 +82,28 @@ static int st_magn_i2c_probe(struct i2c_client *client, if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_magn_common_probe(indio_dev); if (err < 0) - return err; + goto st_magn_power_off; return 0; + +st_magn_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_magn_i2c_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + st_magn_common_remove(indio_dev); return 0; diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 0e2323dfc687..03c0a737aba6 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -76,16 +76,28 @@ static int st_magn_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_magn_common_probe(indio_dev); if (err < 0) - return err; + goto st_magn_power_off; return 0; + +st_magn_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_magn_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + st_magn_common_remove(indio_dev); return 0; diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 2f2f8cb3c26c..9ff7b0e56cf6 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -831,7 +831,7 @@ static int yas5xx_probe(struct i2c_client *i2c, yas5xx->dev = dev; mutex_init(&yas5xx->lock); - ret = iio_read_mount_matrix(dev, "mount-matrix", &yas5xx->orientation); + ret = iio_read_mount_matrix(dev, &yas5xx->orientation); if (ret) return ret; diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index fd77e7ee87f3..738b5f4626ce 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -303,7 +303,6 @@ static int hid_hinge_probe(struct platform_device *pdev) return ret; } - indio_dev->dev.parent = &pdev->dev; indio_dev->info = &hinge_info; indio_dev->name = "hinge"; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 8a9c576616ee..ed30bdaa10ec 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -71,8 +71,8 @@ struct lmp91000_data { struct completion completion; u8 chan_select; - - u32 buffer[4]; /* 64-bit data + 64-bit timestamp */ + /* 64-bit data + 64-bit naturally aligned timestamp */ + u32 buffer[4] __aligned(8); }; static const struct iio_chan_spec lmp91000_channels[] = { @@ -323,7 +323,8 @@ static int lmp91000_probe(struct i2c_client *client, } data->trig = devm_iio_trigger_alloc(dev, "%s-mux%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!data->trig) { dev_err(dev, "cannot allocate iio trigger.\n"); return -ENOMEM; diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 5c746ff6087e..9417b3bd7513 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -41,10 +41,6 @@ static __maybe_unused const struct st_sensors_platform_data default_press_pdata .drdy_int_pin = 1, }; -const struct st_sensor_settings *st_press_get_settings(const char *name); -int st_press_common_probe(struct iio_dev *indio_dev); -void st_press_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_press_allocate_ring(struct iio_dev *indio_dev); void st_press_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 789a2928504a..7912b5a68395 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -689,13 +689,9 @@ int st_press_common_probe(struct iio_dev *indio_dev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &press_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_press_power_off; + return err; /* * Skip timestamping channel while declaring available channels to @@ -718,11 +714,11 @@ int st_press_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_press_power_off; + return err; err = st_press_allocate_ring(indio_dev); if (err < 0) - goto st_press_power_off; + return err; if (press_data->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -745,9 +741,6 @@ st_press_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_press_probe_trigger_error: st_press_deallocate_ring(indio_dev); -st_press_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_press_common_probe); @@ -756,8 +749,6 @@ void st_press_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *press_data = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (press_data->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 09c6903f99b8..f0a5af314ceb 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -98,16 +98,29 @@ static int st_press_i2c_probe(struct i2c_client *client, if (ret < 0) return ret; + ret = st_sensors_power_enable(indio_dev); + if (ret) + return ret; + ret = st_press_common_probe(indio_dev); if (ret < 0) - return ret; + goto st_press_power_off; return 0; + +st_press_power_off: + st_sensors_power_disable(indio_dev); + + return ret; } static int st_press_i2c_remove(struct i2c_client *client) { - st_press_common_remove(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + + st_press_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index b5ee3ec2764f..b48cf7d01cd7 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -82,16 +82,29 @@ static int st_press_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_press_common_probe(indio_dev); if (err < 0) - return err; + goto st_press_power_off; return 0; + +st_press_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_press_spi_remove(struct spi_device *spi) { - st_press_common_remove(spi_get_drvdata(spi)); + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + + st_press_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index a93411216aee..89295c90f801 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -1408,7 +1408,8 @@ static int zpa2326_init_managed_trigger(struct device *parent, return 0; trigger = devm_iio_trigger_alloc(parent, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!trigger) return -ENOMEM; diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index edc4a35ae66d..3797a8f54276 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -59,7 +59,11 @@ struct as3935_state { unsigned long noise_tripped; u32 tune_cap; u32 nflwdth_reg; - u8 buffer[16]; /* 8-bit data + 56-bit padding + 64-bit timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u8 chan; + s64 timestamp __aligned(8); + } scan; u8 buf[2] ____cacheline_aligned; }; @@ -225,8 +229,8 @@ static irqreturn_t as3935_trigger_handler(int irq, void *private) if (ret) goto err_read; - st->buffer[0] = val & AS3935_DATA_MASK; - iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer, + st->scan.chan = val & AS3935_DATA_MASK; + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, iio_get_time_ns(indio_dev)); err_read: iio_trigger_notify_done(indio_dev->trig); @@ -404,7 +408,8 @@ static int as3935_probe(struct spi_device *spi) indio_dev->info = &as3935_info; trig = devm_iio_trigger_alloc(dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!trig) return -ENOMEM; diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index cc206bfa09c7..d854b8d5fbba 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -44,7 +44,11 @@ struct lidar_data { int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len); int i2c_enabled; - u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chan; + s64 timestamp __aligned(8); + } scan; }; static const struct iio_chan_spec lidar_channels[] = { @@ -230,9 +234,9 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private) struct lidar_data *data = iio_priv(indio_dev); int ret; - ret = lidar_get_measurement(data, data->buffer); + ret = lidar_get_measurement(data, &data->scan.chan); if (!ret) { - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); } else if (ret != -EINVAL) { dev_err(&data->client->dev, "cannot read LIDAR measurement"); diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 420c37c72de4..fe88b2bb60bc 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -100,9 +100,11 @@ static int srf04_read(struct srf04_data *data) u64 dt_ns; u32 time_ns, distance_mm; - if (data->gpiod_power) - pm_runtime_get_sync(data->dev); - + if (data->gpiod_power) { + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + } /* * just one read-echo-cycle can take place at a time * ==> lock against concurrent reading calls diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index 70beac5c9c1d..9b0886760f76 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -63,11 +63,11 @@ struct srf08_data { int range_mm; struct mutex lock; - /* - * triggered buffer - * 1x16-bit channel + 3x16 padding + 4x16 timestamp - */ - s16 buffer[8]; + /* Ensure timestamp is naturally aligned */ + struct { + s16 chan; + s64 timestamp __aligned(8); + } scan; /* Sensor-Type */ enum srf08_sensor_type sensor_type; @@ -190,9 +190,9 @@ static irqreturn_t srf08_trigger_handler(int irq, void *p) mutex_lock(&data->lock); - data->buffer[0] = sensor_data; + data->scan.chan = sensor_data; iio_push_to_buffers_with_timestamp(indio_dev, - data->buffer, pf->timestamp); + &data->scan, pf->timestamp); mutex_unlock(&data->lock); err: diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 327ebb7ddbb9..175f3b7c61d7 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -1473,7 +1473,7 @@ static int sx9310_probe(struct i2c_client *client) data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->trig) return -ENOMEM; diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index a87f4a8e4327..3e4ddb2e8c2b 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -946,7 +946,7 @@ static int sx9500_probe(struct i2c_client *client, return ret; data->trig = devm_iio_trigger_alloc(&client->dev, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!data->trig) return -ENOMEM; diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 4df60082c1fa..f20ae3c963cb 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -96,6 +96,16 @@ config TMP007 This driver can also be built as a module. If so, the module will be called tmp007. +config TMP117 + tristate "TMP117 Digital temperature sensor with integrated NV memory" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + TMP117 Digital temperature sensor with integrated NV memory. + + This driver can also be built as a module. If so, the module will + be called tmp117. + config TSYS01 tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 90c113115422..e3392c4b29b4 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o +obj-$(CONFIG_TMP117) += tmp117.o obj-$(CONFIG_TSYS01) += tsys01.o obj-$(CONFIG_TSYS02D) += tsys02d.o diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index ef0fec94d269..afcb10ea7c44 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -176,11 +176,14 @@ static inline s32 mlx90614_iir_search(const struct i2c_client *client, static int mlx90614_power_get(struct mlx90614_data *data, bool startup) { unsigned long now; + int ret; if (!data->wakeup_gpio) return 0; - pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); + if (ret < 0) + return ret; if (startup) { now = jiffies; @@ -267,7 +270,10 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, *val = MLX90614_CONST_SCALE; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */ - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = i2c_smbus_read_word_data(data->client, MLX90614_EMISSIVITY); @@ -287,7 +293,10 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with FIR = 1024 */ - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG); mutex_unlock(&data->lock); @@ -319,7 +328,10 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev, val = val * MLX90614_CONST_RAW_EMISSIVITY_MAX + val2 / MLX90614_CONST_EMISSIVITY_RESOLUTION; - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY, val); @@ -331,7 +343,10 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev, if (val < 0 || val2 < 0) return -EINVAL; - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = mlx90614_iir_search(data->client, val * 100 + val2 / 10000); diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c new file mode 100644 index 000000000000..f9b8f2b570f6 --- /dev/null +++ b/drivers/iio/temperature/tmp117.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital temperature sensor with integrated Non-volatile memory + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + * + * Driver for the Texas Instruments TMP117 Temperature Sensor + * (7-bit I2C slave address (0x48 - 0x4B), changeable via ADD pins) + * + * Note: This driver assumes that the sensor has been calibrated beforehand. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/limits.h> + +#include <linux/iio/iio.h> + +#define TMP117_REG_TEMP 0x0 +#define TMP117_REG_CFGR 0x1 +#define TMP117_REG_HIGH_LIM 0x2 +#define TMP117_REG_LOW_LIM 0x3 +#define TMP117_REG_EEPROM_UL 0x4 +#define TMP117_REG_EEPROM1 0x5 +#define TMP117_REG_EEPROM2 0x6 +#define TMP117_REG_TEMP_OFFSET 0x7 +#define TMP117_REG_EEPROM3 0x8 +#define TMP117_REG_DEVICE_ID 0xF + +#define TMP117_RESOLUTION_10UC 78125 +#define TMP117_DEVICE_ID 0x0117 +#define MICRODEGREE_PER_10MILLIDEGREE 10000 + +struct tmp117_data { + struct i2c_client *client; + s16 calibbias; +}; + +static int tmp117_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct tmp117_data *data = iio_priv(indio_dev); + s32 ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_word_swapped(data->client, + TMP117_REG_TEMP); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 15); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBBIAS: + ret = i2c_smbus_read_word_swapped(data->client, + TMP117_REG_TEMP_OFFSET); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 15); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Conversion from 10s of uC to mC + * as IIO reports temperature in mC + */ + *val = TMP117_RESOLUTION_10UC / MICRODEGREE_PER_10MILLIDEGREE; + *val2 = (TMP117_RESOLUTION_10UC % + MICRODEGREE_PER_10MILLIDEGREE) * 100; + + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int tmp117_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct tmp117_data *data = iio_priv(indio_dev); + s16 off; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + off = clamp_t(int, val, S16_MIN, S16_MAX); + if (off == data->calibbias) + return 0; + data->calibbias = off; + return i2c_smbus_write_word_swapped(data->client, + TMP117_REG_TEMP_OFFSET, off); + + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec tmp117_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_info tmp117_info = { + .read_raw = tmp117_read_raw, + .write_raw = tmp117_write_raw, +}; + +static int tmp117_identify(struct i2c_client *client) +{ + int dev_id; + + dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID); + if (dev_id < 0) + return dev_id; + if (dev_id != TMP117_DEVICE_ID) { + dev_err(&client->dev, "TMP117 not found\n"); + return -ENODEV; + } + return 0; +} + +static int tmp117_probe(struct i2c_client *client) +{ + struct tmp117_data *data; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + ret = tmp117_identify(client); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + data->calibbias = 0; + + indio_dev->name = "tmp117"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tmp117_info; + + indio_dev->channels = tmp117_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id tmp117_of_match[] = { + { .compatible = "ti,tmp117", }, + { } +}; +MODULE_DEVICE_TABLE(of, tmp117_of_match); + +static const struct i2c_device_id tmp117_id[] = { + { "tmp117", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp117_id); + +static struct i2c_driver tmp117_driver = { + .driver = { + .name = "tmp117", + .of_match_table = tmp117_of_match, + }, + .probe_new = tmp117_probe, + .id_table = tmp117_id, +}; +module_i2c_driver(tmp117_driver); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("TI TMP117 Temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 3aa9e8bba005..33083877cd19 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -296,7 +296,7 @@ static ssize_t stm32_tt_show_master_mode(struct device *dev, else cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; - return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]); + return sysfs_emit(buf, "%s\n", master_mode_table[cr2]); } static ssize_t stm32_tt_store_master_mode(struct device *dev, diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index b68304da288b..1d3026dae827 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -5,20 +5,14 @@ * Copyright 2010 Analog Devices Inc. */ -#include <linux/delay.h> #include <linux/device.h> -#include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> -#include <linux/iio/sysfs.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> #define ADIS16203_STARTUP_DELAY 220 /* ms */ diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 8d3afc6dc755..2a8aa83b8d9e 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -5,20 +5,14 @@ * Copyright 2010 Analog Devices Inc. */ -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> #include <linux/sysfs.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #define ADIS16240_STARTUP_DELAY 220 /* ms */ diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index eab534dc4bcc..78ac720266e6 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -18,8 +18,6 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include "ad7746.h" - /* * AD7746 Register Definition */ @@ -84,10 +82,6 @@ #define AD7746_CAPDAC_DACEN BIT(7) #define AD7746_CAPDAC_DACP(x) ((x) & 0x7F) -/* - * struct ad7746_chip_info - chip specific information - */ - struct ad7746_chip_info { struct i2c_client *client; struct mutex lock; /* protect sensor state */ @@ -215,6 +209,19 @@ static const unsigned char ad7746_cap_filter_rate_table[][2] = { {16, 62 + 1}, {13, 77 + 1}, {11, 92 + 1}, {9, 110 + 1}, }; +static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel) +{ + int ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAPDACA, + chip->capdac[channel][0]); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAPDACB, + chip->capdac[channel][1]); +} + static int ad7746_select_channel(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { @@ -230,17 +237,11 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, AD7746_CONF_CAPFS_SHIFT; delay = ad7746_cap_filter_rate_table[idx][1]; + ret = ad7746_set_capdac(chip, chan->channel); + if (ret < 0) + return ret; + if (chip->capdac_set != chan->channel) { - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAPDACA, - chip->capdac[chan->channel][0]); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAPDACB, - chip->capdac[chan->channel][1]); - if (ret < 0) - return ret; chip->capdac_set = chan->channel; } @@ -484,14 +485,7 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, chip->capdac[chan->channel][chan->differential] = val > 0 ? AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0; - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAPDACA, - chip->capdac[chan->channel][0]); - if (ret < 0) - goto out; - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAPDACB, - chip->capdac[chan->channel][1]); + ret = ad7746_set_capdac(chip, chan->channel); if (ret < 0) goto out; @@ -564,10 +558,10 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_TEMP: - /* - * temperature in milli degrees Celsius - * T = ((*val / 2048) - 4096) * 1000 - */ + /* + * temperature in milli degrees Celsius + * T = ((*val / 2048) - 4096) * 1000 + */ *val = (*val * 125) / 256; break; case IIO_VOLTAGE: @@ -669,18 +663,15 @@ static const struct iio_info ad7746_info = { .write_raw = ad7746_write_raw, }; -/* - * device probe and remove - */ - static int ad7746_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ad7746_platform_data *pdata = client->dev.platform_data; + struct device *dev = &client->dev; struct ad7746_chip_info *chip; struct iio_dev *indio_dev; unsigned char regval = 0; - int ret = 0; + unsigned int vdd_permille; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); if (!indio_dev) @@ -702,26 +693,39 @@ static int ad7746_probe(struct i2c_client *client, indio_dev->num_channels = ARRAY_SIZE(ad7746_channels) - 2; indio_dev->modes = INDIO_DIRECT_MODE; - if (pdata) { - if (pdata->exca_en) { - if (pdata->exca_inv_en) - regval |= AD7746_EXCSETUP_NEXCA; - else - regval |= AD7746_EXCSETUP_EXCA; - } + if (device_property_read_bool(dev, "adi,exca-output-en")) { + if (device_property_read_bool(dev, "adi,exca-output-invert")) + regval |= AD7746_EXCSETUP_NEXCA; + else + regval |= AD7746_EXCSETUP_EXCA; + } - if (pdata->excb_en) { - if (pdata->excb_inv_en) - regval |= AD7746_EXCSETUP_NEXCB; - else - regval |= AD7746_EXCSETUP_EXCB; - } + if (device_property_read_bool(dev, "adi,excb-output-en")) { + if (device_property_read_bool(dev, "adi,excb-output-invert")) + regval |= AD7746_EXCSETUP_NEXCB; + else + regval |= AD7746_EXCSETUP_EXCB; + } - regval |= AD7746_EXCSETUP_EXCLVL(pdata->exclvl); - } else { - dev_warn(&client->dev, "No platform data? using default\n"); - regval = AD7746_EXCSETUP_EXCA | AD7746_EXCSETUP_EXCB | - AD7746_EXCSETUP_EXCLVL(3); + ret = device_property_read_u32(dev, "adi,excitation-vdd-permille", + &vdd_permille); + if (!ret) { + switch (vdd_permille) { + case 125: + regval |= AD7746_EXCSETUP_EXCLVL(0); + break; + case 250: + regval |= AD7746_EXCSETUP_EXCLVL(1); + break; + case 375: + regval |= AD7746_EXCSETUP_EXCLVL(2); + break; + case 500: + regval |= AD7746_EXCSETUP_EXCLVL(3); + break; + default: + break; + } } ret = i2c_smbus_write_byte_data(chip->client, @@ -729,11 +733,7 @@ static int ad7746_probe(struct i2c_client *client, if (ret < 0) return ret; - ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); - if (ret) - return ret; - - return 0; + return devm_iio_device_register(indio_dev->dev.parent, indio_dev); } static const struct i2c_device_id ad7746_id[] = { diff --git a/drivers/staging/iio/cdc/ad7746.h b/drivers/staging/iio/cdc/ad7746.h deleted file mode 100644 index 8bdbd732dbbd..000000000000 --- a/drivers/staging/iio/cdc/ad7746.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 - * - * Copyright 2011 Analog Devices Inc. - */ - -#ifndef IIO_CDC_AD7746_H_ -#define IIO_CDC_AD7746_H_ - -/* - * TODO: struct ad7746_platform_data needs to go into include/linux/iio - */ - -#define AD7466_EXCLVL_0 0 /* +-VDD/8 */ -#define AD7466_EXCLVL_1 1 /* +-VDD/4 */ -#define AD7466_EXCLVL_2 2 /* +-VDD * 3/8 */ -#define AD7466_EXCLVL_3 3 /* +-VDD/2 */ - -struct ad7746_platform_data { - unsigned char exclvl; /*Excitation Voltage Level */ - bool exca_en; /* enables EXCA pin as the excitation output */ - bool exca_inv_en; /* enables /EXCA pin as the excitation output */ - bool excb_en; /* enables EXCB pin as the excitation output */ - bool excb_inv_en; /* enables /EXCB pin as the excitation output */ -}; - -#endif /* IIO_CDC_AD7746_H_ */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e8f2ac8c9c3d..7d71131c394e 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -343,7 +343,6 @@ struct i2c_client { }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) -struct i2c_client *i2c_verify_client(struct device *dev); struct i2c_adapter *i2c_verify_adapter(struct device *dev); const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client); @@ -477,6 +476,13 @@ i2c_new_ancillary_device(struct i2c_client *client, u16 default_addr); void i2c_unregister_device(struct i2c_client *client); + +struct i2c_client *i2c_verify_client(struct device *dev); +#else +static inline struct i2c_client *i2c_verify_client(struct device *dev) +{ + return NULL; +} #endif /* I2C */ /* Mainboard arch_initcall() code should register all its I2C devices. diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h index 7ce8a8adad58..c582e1a14232 100644 --- a/include/linux/iio/common/cros_ec_sensors_core.h +++ b/include/linux/iio/common/cros_ec_sensors_core.h @@ -77,7 +77,7 @@ struct cros_ec_sensors_core_state { u16 scale; } calib[CROS_EC_SENSOR_MAX_AXIS]; s8 sign[CROS_EC_SENSOR_MAX_AXIS]; - u8 samples[CROS_EC_SAMPLE_SIZE]; + u8 samples[CROS_EC_SAMPLE_SIZE] __aligned(8); int (*read_ec_sensors_data)(struct iio_dev *indio_dev, unsigned long scan_mask, s16 *data); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 33e939977444..8bdbaf3f3796 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -13,6 +13,7 @@ #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/irqreturn.h> +#include <linux/iio/iio.h> #include <linux/iio/trigger.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> @@ -20,6 +21,8 @@ #include <linux/platform_data/st_sensors_pdata.h> +#define LSM9DS0_IMU_DEV_NAME "lsm9ds0" + /* * Buffer size max case: 2bytes per channel, 3 channels in total + * 8bytes timestamp channel (s64) @@ -46,8 +49,8 @@ #define ST_SENSORS_MAX_NAME 17 #define ST_SENSORS_MAX_4WAI 8 -#define ST_SENSORS_LSM_CHANNELS(device_type, mask, index, mod, \ - ch2, s, endian, rbits, sbits, addr) \ +#define ST_SENSORS_LSM_CHANNELS_EXT(device_type, mask, index, mod, \ + ch2, s, endian, rbits, sbits, addr, ext) \ { \ .type = device_type, \ .modified = mod, \ @@ -63,8 +66,14 @@ .storagebits = sbits, \ .endianness = endian, \ }, \ + .ext_info = ext, \ } +#define ST_SENSORS_LSM_CHANNELS(device_type, mask, index, mod, \ + ch2, s, endian, rbits, sbits, addr) \ + ST_SENSORS_LSM_CHANNELS_EXT(device_type, mask, index, mod, \ + ch2, s, endian, rbits, sbits, addr, NULL) + #define ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL() \ IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \ st_sensors_sysfs_sampling_frequency_avail) @@ -213,6 +222,7 @@ struct st_sensor_settings { * struct st_sensor_data - ST sensor device status * @dev: Pointer to instance of struct device (I2C or SPI). * @trig: The trigger in use by the core driver. + * @mount_matrix: The mounting matrix of the sensor. * @sensor_settings: Pointer to the specific sensor settings in use. * @current_fullscale: Maximum range of measure by the sensor. * @vdd: Pointer to sensor's Vdd power supply @@ -232,7 +242,7 @@ struct st_sensor_settings { struct st_sensor_data { struct device *dev; struct iio_trigger *trig; - struct iio_mount_matrix *mount_matrix; + struct iio_mount_matrix mount_matrix; struct st_sensor_settings *sensor_settings; struct st_sensor_fullscale_avl *current_fullscale; struct regulator *vdd; @@ -317,4 +327,24 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev, void st_sensors_dev_name_probe(struct device *dev, char *name, int len); +/* Accelerometer */ +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); + +/* Gyroscope */ +const struct st_sensor_settings *st_gyro_get_settings(const char *name); +int st_gyro_common_probe(struct iio_dev *indio_dev); +void st_gyro_common_remove(struct iio_dev *indio_dev); + +/* Magnetometer */ +const struct st_sensor_settings *st_magn_get_settings(const char *name); +int st_magn_common_probe(struct iio_dev *indio_dev); +void st_magn_common_remove(struct iio_dev *indio_dev); + +/* Pressure */ +const struct st_sensor_settings *st_press_get_settings(const char *name); +int st_press_common_probe(struct iio_dev *indio_dev); +void st_press_common_remove(struct iio_dev *indio_dev); + #endif /* ST_SENSORS_H */ diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index 32addd5e790e..c9504e9da571 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -6,6 +6,10 @@ /** * struct iio_dev_opaque - industrial I/O device opaque information * @indio_dev: public industrial I/O device information + * @id: used to identify device internally + * @driver_module: used to make it harder to undercut users + * @info_exist_lock: lock to prevent use during removal + * @trig_readonly: mark the current trigger immutable * @event_interface: event chrdevs associated with interrupt lines * @attached_buffers: array of buffers statically attached by the driver * @attached_buffers_cnt: number of buffers in the array of statically attached buffers @@ -19,6 +23,10 @@ * @groupcounter: index of next attribute group * @legacy_scan_el_group: attribute group for legacy scan elements attribute group * @legacy_buffer_group: attribute group for legacy buffer attributes group + * @scan_index_timestamp: cache of the index to the timestamp + * @clock_id: timestamping clock posix identifier + * @chrdev: associated character device + * @flags: file ops related flags including busy flag. * @debugfs_dentry: device specific debugfs dentry * @cached_reg_addr: cached register address for debugfs reads * @read_buf: read buffer to be used for the initial reg read @@ -26,6 +34,10 @@ */ struct iio_dev_opaque { struct iio_dev indio_dev; + int id; + struct module *driver_module; + struct mutex info_exist_lock; + bool trig_readonly; struct iio_event_interface *event_interface; struct iio_buffer **attached_buffers; unsigned int attached_buffers_cnt; @@ -38,6 +50,12 @@ struct iio_dev_opaque { int groupcounter; struct attribute_group legacy_scan_el_group; struct attribute_group legacy_buffer_group; + + unsigned int scan_index_timestamp; + clockid_t clock_id; + struct cdev chrdev; + unsigned long flags; + #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned cached_reg_addr; @@ -46,7 +64,7 @@ struct iio_dev_opaque { #endif }; -#define to_iio_dev_opaque(indio_dev) \ - container_of(indio_dev, struct iio_dev_opaque, indio_dev) +#define to_iio_dev_opaque(_indio_dev) \ + container_of((_indio_dev), struct iio_dev_opaque, indio_dev) #endif diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index f2d65e2e88b6..324561b7a5e8 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -127,8 +127,7 @@ struct iio_mount_matrix { ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv, const struct iio_chan_spec *chan, char *buf); -int iio_read_mount_matrix(struct device *dev, const char *propname, - struct iio_mount_matrix *matrix); +int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix); typedef const struct iio_mount_matrix * (iio_get_mount_matrix_t)(const struct iio_dev *indio_dev, @@ -488,8 +487,6 @@ struct iio_buffer_setup_ops { /** * struct iio_dev - industrial I/O device - * @id: [INTERN] used to identify device internally - * @driver_module: [INTERN] used to make it harder to undercut users * @modes: [DRIVER] operating modes supported by device * @currentmode: [DRIVER] current operating mode * @dev: [DRIVER] device structure, should be assigned a parent @@ -503,9 +500,7 @@ struct iio_buffer_setup_ops { * channels * @active_scan_mask: [INTERN] union of all scan masks requested by buffers * @scan_timestamp: [INTERN] set if any buffers have requested timestamp - * @scan_index_timestamp:[INTERN] cache of the index to the timestamp * @trig: [INTERN] current device trigger (buffer modes) - * @trig_readonly: [INTERN] mark the current trigger immutable * @pollfunc: [DRIVER] function run on trigger being received * @pollfunc_event: [DRIVER] function run on events trigger being received * @channels: [DRIVER] channel specification structure table @@ -513,19 +508,12 @@ struct iio_buffer_setup_ops { * @name: [DRIVER] name of the device. * @label: [DRIVER] unique name to identify which device this is * @info: [DRIVER] callbacks and constant info from driver - * @clock_id: [INTERN] timestamping clock posix identifier - * @info_exist_lock: [INTERN] lock to prevent use during removal * @setup_ops: [DRIVER] callbacks to call before and after buffer * enable/disable - * @chrdev: [INTERN] associated character device - * @flags: [INTERN] file ops related flags including busy flag. * @priv: [DRIVER] reference to driver's private information * **MUST** be accessed **ONLY** via iio_priv() helper */ struct iio_dev { - int id; - struct module *driver_module; - int modes; int currentmode; struct device dev; @@ -538,9 +526,7 @@ struct iio_dev { unsigned masklength; const unsigned long *active_scan_mask; bool scan_timestamp; - unsigned scan_index_timestamp; struct iio_trigger *trig; - bool trig_readonly; struct iio_poll_func *pollfunc; struct iio_poll_func *pollfunc_event; @@ -550,15 +536,13 @@ struct iio_dev { const char *name; const char *label; const struct iio_info *info; - clockid_t clock_id; - struct mutex info_exist_lock; const struct iio_buffer_setup_ops *setup_ops; - struct cdev chrdev; - unsigned long flags; void *priv; }; +int iio_device_id(struct iio_dev *indio_dev); + const struct iio_chan_spec *iio_find_channel_from_si(struct iio_dev *indio_dev, int si); /** @@ -602,15 +586,7 @@ static inline void iio_device_put(struct iio_dev *indio_dev) put_device(&indio_dev->dev); } -/** - * iio_device_get_clock() - Retrieve current timestamping clock for the device - * @indio_dev: IIO device structure containing the device - */ -static inline clockid_t iio_device_get_clock(const struct iio_dev *indio_dev) -{ - return indio_dev->clock_id; -} - +clockid_t iio_device_get_clock(const struct iio_dev *indio_dev); int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id); /** diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index f9b728d490b1..cf49997d5903 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -55,6 +55,7 @@ struct adis_timeout { * this should be the minimum size supported by the device. * @burst_max_len: Holds the maximum burst size when the device supports * more than one burst mode with different sizes + * @burst_max_speed_hz: Maximum spi speed that can be used in burst mode */ struct adis_data { unsigned int read_delay; @@ -83,6 +84,7 @@ struct adis_data { unsigned int burst_reg_cmd; unsigned int burst_len; unsigned int burst_max_len; + unsigned int burst_max_speed_hz; }; /** diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h index e40b28ca892e..897051e51b78 100644 --- a/include/linux/platform_data/st_sensors_pdata.h +++ b/include/linux/platform_data/st_sensors_pdata.h @@ -13,8 +13,9 @@ /** * struct st_sensors_platform_data - Platform data for the ST sensors * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). - * Available only for accelerometer and pressure sensors. + * Available only for accelerometer, magnetometer and pressure sensors. * Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet). + * Magnetometer DRDY is supported only on LSM9DS0. * @open_drain: set the interrupt line to be open drain if possible. * @spi_3wire: enable spi-3wire mode. * @pullups: enable/disable i2c controller pullup resistors. |