mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-02 16:44:10 +08:00
First round of IIO new device support, features and cleanups for the 4.10 cycle.
Fair number of outreachy related patches in here. Some of these may well have already been picked up by Greg but git will sort that out for us. Also some good staging cleanup work from other sources. Thanks Brian and Lars in particular for this. New device support * ACCES 104-quad-8 - New driver for this 8 channel encoder input board. Lots of new ABI with this one. * AD7766 - New driver supporting AD7766, AD7766-1, AD7766-2, AD7767, AD7767-1 and AD7767-2 24 bit ADCs. * dmard 10 - New driver for this 3 axis accelerometer. * Honeywell ABP pressure sensors. - New driver covering 56 parts in this series (too many to list here!) * HTS221 - New driver to support this relative humidiy and temperature device. * LMP91000 - New driver for this potentiostat (form of chemical sensor). Nice example of use of the buffered consumer interfaces and the use of a consumer provided trigger. * MiraMEMS DA311 - New driver for this 3 axis accelerometer. * MiraMEMS DA280 - New driver for this 3 axis accelerometer. Follow up caught up with vendor prefixes for these. Staging graduations * isl29018 light sensor - Fixes and cleanups listed below (thanks for your hard work on this Brian!) * sca3000 - Fixes and cleanups listed below. This was one of the small set of drivers that went into staging when IIO was first added. Turns out it had a few bugs and needed to be brought into the modern era! Not clear if I am the only person who actually has one of these still wired to a board. New features (Core) - Add an iio_trigger_validate_own_device helper which relies on the device and trigger having the same parent. Convenient to have this for some of the more complex trigger / device interactions. Was hand rolled in a few drivers already so good to bring it into the core. - Add an iio_read_channel_offset in kernel access helper (similar to the existing one for scale). - IIO_ATTR_{RO, WO, RW} and IIO_DEVICE_ATTR_{RO, WO, RW} macros. These lead some rather contrived function naming, but there is no denying they do reduced boilerplate. I'm going to resist their introduction in drivers 'unless' they form part of a larger set of cleanups. - Counter channel type and index type. New features (Drivers) * hdc100x - Triggered buffer support. * mcp4725 - Device tree bindings and support. - Voltage reference selection. * ti-adc0832 - Triggered buffer support. * ti-adc161s626 - Add regulator support allowing _scale and _offset values to be established and exported. New features (Tools) * iio_generic_buffer - -A option to force enable all channels rather than faulting if some are already enabled (like -a does). Followup patches tidied this support up. Cleanups (Core) - Use kmalloc_array in iio_scan_mask_set. - Take event_attrs field of iio_info structure constant - Staging todo list updates. Most of it was long done. - MAINTAINERS had a wrong directory listing. Cleanups (Drivers) * Missing i2c trivial devices entries. * ad5592r - Fix an endian type related sparse warnings. * ad7150 - Constify the event attribute_group structures. * ad7152 - Add some blank lines to improve readability. - Sampling frequency control via chan-info element rather than hand rolled attributes. - add a new lock to avoid use of mlock for non state change related locking. * ad7280 - Constify atrribute_group structure (second patch covers the event ones) * ad7606 (Lars is driving most of the cleanup on this with some additions from Eva) - Fix improper setting of oversampling pins. This has been broken a very long time in this staging driver, so not going to push this back to stable. - Implement oversampling configuration via the chan_info mask element. - Remove an unused int_vref_mv field. - Remove a reundant name field from ad7606_chip_info. - Remove default device configuration from platform_data in favour of whatever the power on defaults are. - Remove out of band error reporting in the kernel log as not providing much information. - Fix oversampling ratio by having 1 be the value for no oversampling. - Avoid allocating buffer for each data capture. - Factor out common code between periodic and one-shot capture. - Move set_drvdat into common code. - Let the common probe function return int rather than jumping through an ERR_PTR. - Pass struct device * into common remove to simplify code. - Always run trigger handler only once per event (no one can remember why it was being possibly done twice). - Move over to the GPIO descriptor API to shorten and clarify code. - Move the buffer code into the main file as it's not optional and is now rather short in this driver. - Fix the naming of the supply regulator. - Rework regulator handling to handle errors including deferred probing. - Tidy up a ptr_err or 0 return. * ad7746 - Sampling frequency control via info_mask element rather than hand rolled * ad7758 - Sampling frequency control via info_mask element rather than hand rolled attributes. * ad7816 - Constify the event attribute_group structure. * adt7316 - Constify the event attribute group structures. * ak8974 - Cleanup some sparse warnings about endian types. * ak8975 - Cleanup some sparse warnings about endian types. * bmi160 - Spare endian warning cleanups. * isl29018 (towards staging graduation) - Remove unusedvariables and defines. - Improve consistency of error handling. - Signed / unsigned comparison fixes. - Use the IIO_DEVICE_ATTR_{RO, RW} macros - Fix a race in in_illuminance_scale_available_show. - Cleanup exit points of _read_raw - Sanity check if in suspended state during a write_raw call as was already done for read_raw. - Document device tree bidnings. - Document infrared supression controls. - Add some newlines to improve readability and drop one that shouldn't be there. - Fix a poorly named functions name. - Fix multiline coment syntax. - Tidy up a pair or return statements by unifying them. - Rename description in Kconfig for consistency with similar drivers. * lidar - cleanup power management by dropping unnecessary call. * ltr501 - Use the claim_direct_mode helpers. Fix a race condition along the way. * max1027 - Fix a dubious x | !y sparse warning. - Use the new iio_trigger_validate_own_device helper. * max440000 - Clean up some sparse warnings about endian types. * mcp4725 - Use the regulator framework to establish the reference voltage rather than getting it from platform data. - Tidy up a comment typo. - Fix a wrong PTR_ERR query (wrong regulator). * mma7660 - Take a mma7660_nscale static. * mma8452 - Use the new iio_trigger_validate_own_device helper. - Use claim_direct_mode helpers - fix a race condition along the way. * mpl3115 - Use claim_direct_mode helpers - fix a race condition along the way. * ms65611 - Tidy up regulator error handling and clean out a static warning in the mix. * sca3000 - Avoid a potential unitialized variable if a hardware read returns a value that isn't actually supported (mostly warning supression). - Fix a use before setting of the indio_dev->buffer pointer. Broken for a very long time so not going to rush this into stable. - Merge buffer file with core file. We used to always split these. Sometimes it's just not worth the hassle. In this case the device's main feature is it's hardware fifos so unlikely anyone would want to run it without. - Drop the sca3000_register_ring_funcs function as it's a pointless wrapper once we have only one file. - Fix cleaning of flag + setting of size of scan. Without this you can't start the buffer twice and expect sensible (or any) results. Again, broken for a long time so not heading for stable. - Drop the custom watershed setting ABI - for now we'll just support one value. - Move to a hybrid hard / soft buffer design (how we've been doing it for similar devices for a while now!) - Cleanup some unusued variables. - Use a fake channel to support core handling of freefall event registration. - Cleanup the register defines. - Fix an off by one error in axis due to IIO_NO_MOD taking up the 0 value. Been broken since first admission of IIO to the staging tree. - Add readback of the 3db low pass filter frequency and later writing allowing droppign of custom measurement mode attributes as they can be represented by the filter choices that is their main characteristic. - Drop non standard revision attr and replace with dev_info on probe. - Avoid a race in probe. - Various formatting fixes. - Kernel-docify docs that were very nearly in the write format. * tsl2583 - Constify attribute_group structure. * zpa2326 - Drop a redundant DEBUG ifdef. Cleanups (Tools) * iio_generic_buffer - Fix the ? arguement. Previously it sort of worked as you got the help message as a result of it not recognising the arguement. -----BEGIN PGP SIGNATURE----- iQIuBAABCAAYBQJYDSX+ERxqaWMyM0BrZXJuZWwub3JnAAoJEFSFNJnE9BaIQSEP /2HRMqDXeIh2N0oVLBFK5pEaAZ52tjsEmf1XgfCJ3D1PczS0vzqkFKuG/078BU3v e9MTj39sF1EOyGJv4mdfnHygLPvjaTU/A6HoIaP1t9cUgSQ1jzPN0tO8/Hj/ESlU U3mNgUNUIa9cnVIfWk2cPJqgKJ/w52vRk00yRnOKZjHZkxbYQq9rPJ7ks12jQSGT IBEipiYMnRovc2w4NWbAry5pAXBmttVFKyi5FZ7uIM5EH34+SAY6laXSoZQl8E74 LKgIwdPUn9yeujyX6ohBFRFloOf/4VYedV6NIUz8k5BzuTeAJcp4DSMLXSWIwZVd sdza6el3Tt0MkjIgioTg7N07FvatbQVIsnWnLjLb/zpMjGQPigfTnIi7kYcH3wHx NwpTy1MZg5U571LOYXsEhNvIFiQICoV6+8d/PzrO1J+rphuQYreOm0WzjvIqW4q/ dU2uCAMOHd82gjm9QrKjvrc+4M3oGc3ENE/i5P2YNQhdkdNKp9PMWgL6RO9QXgeQ 9TrjbDr9OSFLatZg5IswWFa5CHL9AZ6OuvaRyx/8n5cBdY3n8+wMIowRQJlMUdGY Aa5ByntMsvhwCu+M1+fDihZyx4suTNvW7+Emqd/8lCb+BRjHoamBb6uGBGx6Txvy 6TPluNWE+cK5Tjkrn+jDbTzhwdaaSRtO4ECRwxXYAqfn =ZRUt -----END PGP SIGNATURE----- Merge tag 'iio-for-4.10a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of IIO new device support, features and cleanups for the 4.10 cycle. Fair number of outreachy related patches in here. Some of these may well have already been picked up by Greg but git will sort that out for us. Also some good staging cleanup work from other sources. Thanks Brian and Lars in particular for this. New device support * ACCES 104-quad-8 - New driver for this 8 channel encoder input board. Lots of new ABI with this one. * AD7766 - New driver supporting AD7766, AD7766-1, AD7766-2, AD7767, AD7767-1 and AD7767-2 24 bit ADCs. * dmard 10 - New driver for this 3 axis accelerometer. * Honeywell ABP pressure sensors. - New driver covering 56 parts in this series (too many to list here!) * HTS221 - New driver to support this relative humidiy and temperature device. * LMP91000 - New driver for this potentiostat (form of chemical sensor). Nice example of use of the buffered consumer interfaces and the use of a consumer provided trigger. * MiraMEMS DA311 - New driver for this 3 axis accelerometer. * MiraMEMS DA280 - New driver for this 3 axis accelerometer. Follow up caught up with vendor prefixes for these. Staging graduations * isl29018 light sensor - Fixes and cleanups listed below (thanks for your hard work on this Brian!) * sca3000 - Fixes and cleanups listed below. This was one of the small set of drivers that went into staging when IIO was first added. Turns out it had a few bugs and needed to be brought into the modern era! Not clear if I am the only person who actually has one of these still wired to a board. New features (Core) - Add an iio_trigger_validate_own_device helper which relies on the device and trigger having the same parent. Convenient to have this for some of the more complex trigger / device interactions. Was hand rolled in a few drivers already so good to bring it into the core. - Add an iio_read_channel_offset in kernel access helper (similar to the existing one for scale). - IIO_ATTR_{RO, WO, RW} and IIO_DEVICE_ATTR_{RO, WO, RW} macros. These lead some rather contrived function naming, but there is no denying they do reduced boilerplate. I'm going to resist their introduction in drivers 'unless' they form part of a larger set of cleanups. - Counter channel type and index type. New features (Drivers) * hdc100x - Triggered buffer support. * mcp4725 - Device tree bindings and support. - Voltage reference selection. * ti-adc0832 - Triggered buffer support. * ti-adc161s626 - Add regulator support allowing _scale and _offset values to be established and exported. New features (Tools) * iio_generic_buffer - -A option to force enable all channels rather than faulting if some are already enabled (like -a does). Followup patches tidied this support up. Cleanups (Core) - Use kmalloc_array in iio_scan_mask_set. - Take event_attrs field of iio_info structure constant - Staging todo list updates. Most of it was long done. - MAINTAINERS had a wrong directory listing. Cleanups (Drivers) * Missing i2c trivial devices entries. * ad5592r - Fix an endian type related sparse warnings. * ad7150 - Constify the event attribute_group structures. * ad7152 - Add some blank lines to improve readability. - Sampling frequency control via chan-info element rather than hand rolled attributes. - add a new lock to avoid use of mlock for non state change related locking. * ad7280 - Constify atrribute_group structure (second patch covers the event ones) * ad7606 (Lars is driving most of the cleanup on this with some additions from Eva) - Fix improper setting of oversampling pins. This has been broken a very long time in this staging driver, so not going to push this back to stable. - Implement oversampling configuration via the chan_info mask element. - Remove an unused int_vref_mv field. - Remove a reundant name field from ad7606_chip_info. - Remove default device configuration from platform_data in favour of whatever the power on defaults are. - Remove out of band error reporting in the kernel log as not providing much information. - Fix oversampling ratio by having 1 be the value for no oversampling. - Avoid allocating buffer for each data capture. - Factor out common code between periodic and one-shot capture. - Move set_drvdat into common code. - Let the common probe function return int rather than jumping through an ERR_PTR. - Pass struct device * into common remove to simplify code. - Always run trigger handler only once per event (no one can remember why it was being possibly done twice). - Move over to the GPIO descriptor API to shorten and clarify code. - Move the buffer code into the main file as it's not optional and is now rather short in this driver. - Fix the naming of the supply regulator. - Rework regulator handling to handle errors including deferred probing. - Tidy up a ptr_err or 0 return. * ad7746 - Sampling frequency control via info_mask element rather than hand rolled * ad7758 - Sampling frequency control via info_mask element rather than hand rolled attributes. * ad7816 - Constify the event attribute_group structure. * adt7316 - Constify the event attribute group structures. * ak8974 - Cleanup some sparse warnings about endian types. * ak8975 - Cleanup some sparse warnings about endian types. * bmi160 - Spare endian warning cleanups. * isl29018 (towards staging graduation) - Remove unusedvariables and defines. - Improve consistency of error handling. - Signed / unsigned comparison fixes. - Use the IIO_DEVICE_ATTR_{RO, RW} macros - Fix a race in in_illuminance_scale_available_show. - Cleanup exit points of _read_raw - Sanity check if in suspended state during a write_raw call as was already done for read_raw. - Document device tree bidnings. - Document infrared supression controls. - Add some newlines to improve readability and drop one that shouldn't be there. - Fix a poorly named functions name. - Fix multiline coment syntax. - Tidy up a pair or return statements by unifying them. - Rename description in Kconfig for consistency with similar drivers. * lidar - cleanup power management by dropping unnecessary call. * ltr501 - Use the claim_direct_mode helpers. Fix a race condition along the way. * max1027 - Fix a dubious x | !y sparse warning. - Use the new iio_trigger_validate_own_device helper. * max440000 - Clean up some sparse warnings about endian types. * mcp4725 - Use the regulator framework to establish the reference voltage rather than getting it from platform data. - Tidy up a comment typo. - Fix a wrong PTR_ERR query (wrong regulator). * mma7660 - Take a mma7660_nscale static. * mma8452 - Use the new iio_trigger_validate_own_device helper. - Use claim_direct_mode helpers - fix a race condition along the way. * mpl3115 - Use claim_direct_mode helpers - fix a race condition along the way. * ms65611 - Tidy up regulator error handling and clean out a static warning in the mix. * sca3000 - Avoid a potential unitialized variable if a hardware read returns a value that isn't actually supported (mostly warning supression). - Fix a use before setting of the indio_dev->buffer pointer. Broken for a very long time so not going to rush this into stable. - Merge buffer file with core file. We used to always split these. Sometimes it's just not worth the hassle. In this case the device's main feature is it's hardware fifos so unlikely anyone would want to run it without. - Drop the sca3000_register_ring_funcs function as it's a pointless wrapper once we have only one file. - Fix cleaning of flag + setting of size of scan. Without this you can't start the buffer twice and expect sensible (or any) results. Again, broken for a long time so not heading for stable. - Drop the custom watershed setting ABI - for now we'll just support one value. - Move to a hybrid hard / soft buffer design (how we've been doing it for similar devices for a while now!) - Cleanup some unusued variables. - Use a fake channel to support core handling of freefall event registration. - Cleanup the register defines. - Fix an off by one error in axis due to IIO_NO_MOD taking up the 0 value. Been broken since first admission of IIO to the staging tree. - Add readback of the 3db low pass filter frequency and later writing allowing droppign of custom measurement mode attributes as they can be represented by the filter choices that is their main characteristic. - Drop non standard revision attr and replace with dev_info on probe. - Avoid a race in probe. - Various formatting fixes. - Kernel-docify docs that were very nearly in the write format. * tsl2583 - Constify attribute_group structure. * zpa2326 - Drop a redundant DEBUG ifdef. Cleanups (Tools) * iio_generic_buffer - Fix the ? arguement. Previously it sort of worked as you got the help message as a result of it not recognising the arguement.
This commit is contained in:
commit
c7c88e998a
@ -329,6 +329,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -1579,3 +1580,20 @@ Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no offset etc.) electric conductivity reading that
|
||||
can be processed to siemens per meter.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw counter device counts from channel Y. For quadrature
|
||||
counters, multiplication by an available [Y]_scale results in
|
||||
the counts of a single quadrature signal phase from channel Y.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_raw
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw counter device index value from channel Y. This attribute
|
||||
provides an absolute positional reference (e.g. a pulse once per
|
||||
revolution) which may be used to home positional systems as
|
||||
required.
|
||||
|
125
Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8
Normal file
125
Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8
Normal file
@ -0,0 +1,125 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Discrete set of available values for the respective counter
|
||||
configuration are listed in this file.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates whether the counter for
|
||||
channel Y is counting up or down.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count mode for channel Y. Four count modes are available:
|
||||
normal, range limit, non-recycle, and modulo-n. The preset value
|
||||
for channel Y is used by the count mode where required.
|
||||
|
||||
Normal:
|
||||
Counting is continuous in either direction.
|
||||
|
||||
Range Limit:
|
||||
An upper or lower limit is set, mimicking limit switches
|
||||
in the mechanical counterpart. The upper limit is set to
|
||||
the preset value, while the lower limit is set to 0. The
|
||||
counter freezes at count = preset when counting up, and
|
||||
at count = 0 when counting down. At either of these
|
||||
limits, the counting is resumed only when the count
|
||||
direction is reversed.
|
||||
|
||||
Non-recycle:
|
||||
Counter is disabled whenever a 24-bit count overflow or
|
||||
underflow takes place. The counter is re-enabled when a
|
||||
new count value is loaded to the counter via a preset
|
||||
operation or write to raw.
|
||||
|
||||
Modulo-N:
|
||||
A count boundary is set between 0 and the preset value.
|
||||
The counter is reset to 0 at count = preset when
|
||||
counting up, while the counter is set to the preset
|
||||
value at count = 0 when counting down; the counter does
|
||||
not freeze at the bundary points, but counts
|
||||
continuously throughout.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates whether excessive noise is
|
||||
present at the channel Y count inputs in quadrature clock mode;
|
||||
irrelevant in non-quadrature clock mode.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If the counter device supports preset registers, the preset
|
||||
count for channel Y is provided by this attribute.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Configure channel Y counter for non-quadrature or quadrature
|
||||
clock mode. Selecting non-quadrature clock mode will disable
|
||||
synchronous load mode. In quadrature clock mode, the channel Y
|
||||
scale attribute selects the encoder phase division (scale of 1
|
||||
selects full-cycle, scale of 0.5 selects half-cycle, scale of
|
||||
0.25 selects quarter-cycle) processed by the channel Y counter.
|
||||
|
||||
Non-quadrature:
|
||||
The filter and decoder circuit are bypassed. Encoder A
|
||||
input serves as the count input and B as the UP/DOWN
|
||||
direction control input, with B = 1 selecting UP Count
|
||||
mode and B = 0 selecting Down Count mode.
|
||||
|
||||
Quadrature:
|
||||
Encoder A and B inputs are digitally filtered and
|
||||
decoded for UP/DN clock.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Whether to set channel Y counter with channel Y preset value
|
||||
when channel Y index input is active, or continuously count.
|
||||
Valid attribute values are boolean.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Active level of channel Y index input; irrelevant in
|
||||
non-synchronous load mode.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Configure channel Y counter for non-synchronous or synchronous
|
||||
load mode. Synchronous load mode cannot be selected in
|
||||
non-quadrature clock mode.
|
||||
|
||||
Non-synchronous:
|
||||
A logic low level is the active level at this index
|
||||
input. The index function (as enabled via
|
||||
set_to_preset_on_index) is performed directly on the
|
||||
active level of the index input.
|
||||
|
||||
Synchronous:
|
||||
Intended for interfacing with encoder Index output in
|
||||
quadrature clock mode. The active level is configured
|
||||
via index_polarity. The index function (as enabled via
|
||||
set_to_preset_on_index) is performed synchronously with
|
||||
the quadrature clock on the active level of the index
|
||||
input.
|
19
Documentation/ABI/testing/sysfs-bus-iio-light-isl29018
Normal file
19
Documentation/ABI/testing/sysfs-bus-iio-light-isl29018
Normal file
@ -0,0 +1,19 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/proximity_on_chip_ambient_infrared_suppression
|
||||
Date: January 2011
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
|
||||
infrared suppression:
|
||||
|
||||
Scheme 0, makes full n (4, 8, 12, 16) bits (unsigned) proximity
|
||||
detection. The range of Scheme 0 proximity count is from 0 to
|
||||
2^n. Logic 1 of this bit, Scheme 1, makes n-1 (3, 7, 11, 15)
|
||||
bits (2's complementary) proximity_less_ambient detection. The
|
||||
range of Scheme 1 proximity count is from -2^(n-1) to 2^(n-1).
|
||||
The sign bit is extended for resolutions less than 16. While
|
||||
Scheme 0 has wider dynamic range, Scheme 1 proximity detection
|
||||
is less affected by the ambient IR noise variation.
|
||||
|
||||
0 Sensing IR from LED and ambient
|
||||
1 Sensing IR from LED with ambient IR rejection
|
@ -39,11 +39,13 @@ dallas,ds75 Digital Thermometer and Thermostat
|
||||
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
||||
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
||||
domintech,dmard09 DMARD09: 3-axis Accelerometer
|
||||
domintech,dmard10 DMARD10: 3-axis Accelerometer
|
||||
epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
|
||||
fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
|
||||
fsl,mma7660 MMA7660FC: 3-Axis Orientation/Motion Detection Sensor
|
||||
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
|
||||
fsl,mpl3115 MPL3115: Absolute Digital Pressure Sensor
|
||||
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
|
||||
@ -57,6 +59,7 @@ maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
||||
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
|
||||
mc,rv3029c2 Real Time Clock Module with I2C-Bus
|
||||
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
|
||||
memsic,mxc6225 MEMSIC 2-axis 8-bit digital accelerometer
|
||||
microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k)
|
||||
microchip,mcp4531-103 Microchip 7-bit Single I2C Digital Potentiometer (10k)
|
||||
microchip,mcp4531-503 Microchip 7-bit Single I2C Digital Potentiometer (50k)
|
||||
@ -121,6 +124,9 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
|
||||
microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
|
||||
microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
|
||||
microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
|
||||
miramems,da226 MiraMEMS DA226 2-axis 14-bit digital accelerometer
|
||||
miramems,da280 MiraMEMS DA280 3-axis 14-bit digital accelerometer
|
||||
miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer
|
||||
national,lm63 Temperature sensor with integrated fan control
|
||||
national,lm75 I2C TEMP SENSOR
|
||||
national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
|
||||
|
@ -3,6 +3,7 @@
|
||||
Required properties:
|
||||
- compatible: Should be "ti,adc141s626" or "ti,adc161s626"
|
||||
- reg: spi chip select number for the device
|
||||
- vdda-supply: supply voltage to VDDA pin
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
@ -11,6 +12,7 @@ Recommended properties:
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,adc161s626";
|
||||
vdda-supply = <&vdda_fixed>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4300000>;
|
||||
};
|
||||
|
35
Documentation/devicetree/bindings/iio/dac/mcp4725.txt
Normal file
35
Documentation/devicetree/bindings/iio/dac/mcp4725.txt
Normal file
@ -0,0 +1,35 @@
|
||||
Microchip mcp4725 and mcp4726 DAC device driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "microchip,mcp4725" or "microchip,mcp4726"
|
||||
- reg: Should contain the DAC I2C address
|
||||
- vdd-supply: Phandle to the Vdd power supply. This supply is used as a
|
||||
voltage reference on mcp4725. It is used as a voltage reference on
|
||||
mcp4726 if there is no vref-supply specified.
|
||||
|
||||
Optional properties (valid only for mcp4726):
|
||||
- vref-supply: Optional phandle to the Vref power supply. Vref pin is
|
||||
used as a voltage reference when this supply is specified.
|
||||
- microchip,vref-buffered: Boolean to enable buffering of the external
|
||||
Vref pin. This boolean is not valid without the vref-supply. Quoting
|
||||
the datasheet: This is offered in cases where the reference voltage
|
||||
does not have the current capability not to drop its voltage when
|
||||
connected to the internal resistor ladder circuit.
|
||||
|
||||
Examples:
|
||||
|
||||
/* simple mcp4725 */
|
||||
mcp4725@60 {
|
||||
compatible = "microchip,mcp4725";
|
||||
reg = <0x60>;
|
||||
vdd-supply = <&vdac_vdd>;
|
||||
};
|
||||
|
||||
/* mcp4726 with the buffered external reference voltage */
|
||||
mcp4726@60 {
|
||||
compatible = "microchip,mcp4726";
|
||||
reg = <0x60>;
|
||||
vdd-supply = <&vdac_vdd>;
|
||||
vref-supply = <&vdac_vref>;
|
||||
microchip,vref-buffered;
|
||||
};
|
22
Documentation/devicetree/bindings/iio/humidity/hts221.txt
Normal file
22
Documentation/devicetree/bindings/iio/humidity/hts221.txt
Normal file
@ -0,0 +1,22 @@
|
||||
* HTS221 STM humidity + temperature sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "st,hts221"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
hts221@5f {
|
||||
compatible = "st,hts221";
|
||||
reg = <0x5f>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
28
Documentation/devicetree/bindings/iio/light/isl29018.txt
Normal file
28
Documentation/devicetree/bindings/iio/light/isl29018.txt
Normal file
@ -0,0 +1,28 @@
|
||||
* ISL 29018/29023/29035 I2C ALS, Proximity, and Infrared sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"isil,isl29018"
|
||||
"isil,isl29023"
|
||||
"isil,isl29035"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor.
|
||||
|
||||
Example:
|
||||
|
||||
isl29018@44 {
|
||||
compatible = "isil,isl29018";
|
||||
reg = <0x44>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <TEGRA_GPIO(Z, 2) IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
* Texas Instruments LMP91000 potentiostat
|
||||
|
||||
http://www.ti.com/lit/ds/symlink/lmp91000.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,lmp91000"
|
||||
- reg: the I2C address of the device
|
||||
- io-channels: the phandle of the iio provider
|
||||
|
||||
- ti,external-tia-resistor: if the property ti,tia-gain-ohm is not defined this
|
||||
needs to be set to signal that an external resistor value is being used.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- ti,tia-gain-ohm: ohm value of the internal resistor for the transimpedance
|
||||
amplifier. Must be 2750, 3500, 7000, 14000, 35000, 120000, or 350000 ohms.
|
||||
|
||||
- ti,rload-ohm: ohm value of the internal resistor load applied to the gas
|
||||
sensor. Must be 10, 33, 50, or 100 (default) ohms.
|
||||
|
||||
Example:
|
||||
|
||||
lmp91000@48 {
|
||||
compatible = "ti,lmp91000";
|
||||
reg = <0x48>;
|
||||
ti,tia-gain-ohm = <7500>;
|
||||
ti,rload = <100>;
|
||||
io-channels = <&adc>;
|
||||
};
|
@ -160,16 +160,19 @@ lltc Linear Technology Corporation
|
||||
lsi LSI Corp. (LSI Logic)
|
||||
marvell Marvell Technology Group Ltd.
|
||||
maxim Maxim Integrated Products
|
||||
mcube mCube
|
||||
meas Measurement Specialties
|
||||
mediatek MediaTek Inc.
|
||||
melexis Melexis N.V.
|
||||
melfas MELFAS Inc.
|
||||
memsic MEMSIC Inc.
|
||||
merrii Merrii Technology Co., Ltd.
|
||||
micrel Micrel Inc.
|
||||
microchip Microchip Technology Inc.
|
||||
microcrystal Micro Crystal AG
|
||||
micron Micron Technology Inc.
|
||||
minix MINIX Technology Ltd.
|
||||
miramems MiraMEMS Sensing Technology Co., Ltd.
|
||||
mitsubishi Mitsubishi Electric Corporation
|
||||
mosaixtech Mosaix Technologies, Inc.
|
||||
moxa Moxa
|
||||
|
@ -255,6 +255,12 @@ L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-104-idio-16.c
|
||||
|
||||
ACCES 104-QUAD-8 IIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/counter/104-quad-8.c
|
||||
|
||||
ACENIC DRIVER
|
||||
M: Jes Sorensen <jes@trained-monkey.org>
|
||||
L: linux-acenic@sunsite.dk
|
||||
@ -787,7 +793,7 @@ S: Supported
|
||||
F: drivers/iio/*/ad*
|
||||
X: drivers/iio/*/adjd*
|
||||
F: drivers/staging/iio/*/ad*
|
||||
F: staging/iio/trigger/iio-trig-bfin-timer.c
|
||||
F: drivers/staging/iio/trigger/iio-trig-bfin-timer.c
|
||||
|
||||
ANALOG DEVICES INC DMA DRIVERS
|
||||
M: Lars-Peter Clausen <lars@metafoo.de>
|
||||
|
@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig"
|
||||
source "drivers/iio/amplifiers/Kconfig"
|
||||
source "drivers/iio/chemical/Kconfig"
|
||||
source "drivers/iio/common/Kconfig"
|
||||
source "drivers/iio/counter/Kconfig"
|
||||
source "drivers/iio/dac/Kconfig"
|
||||
source "drivers/iio/dummy/Kconfig"
|
||||
source "drivers/iio/frequency/Kconfig"
|
||||
@ -87,6 +88,7 @@ if IIO_TRIGGER
|
||||
source "drivers/iio/trigger/Kconfig"
|
||||
endif #IIO_TRIGGER
|
||||
source "drivers/iio/potentiometer/Kconfig"
|
||||
source "drivers/iio/potentiostat/Kconfig"
|
||||
source "drivers/iio/pressure/Kconfig"
|
||||
source "drivers/iio/proximity/Kconfig"
|
||||
source "drivers/iio/temperature/Kconfig"
|
||||
|
@ -18,6 +18,7 @@ obj-y += amplifiers/
|
||||
obj-y += buffer/
|
||||
obj-y += chemical/
|
||||
obj-y += common/
|
||||
obj-y += counter/
|
||||
obj-y += dac/
|
||||
obj-y += dummy/
|
||||
obj-y += gyro/
|
||||
@ -29,6 +30,7 @@ obj-y += light/
|
||||
obj-y += magnetometer/
|
||||
obj-y += orientation/
|
||||
obj-y += potentiometer/
|
||||
obj-y += potentiostat/
|
||||
obj-y += pressure/
|
||||
obj-y += proximity/
|
||||
obj-y += temperature/
|
||||
|
@ -52,6 +52,26 @@ config BMC150_ACCEL_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config DA280
|
||||
tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the MiraMEMS DA280 3-axis 14-bit
|
||||
digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called da280.
|
||||
|
||||
config DA311
|
||||
tristate "MiraMEMS DA311 3-axis 12-bit digital accelerometer driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the MiraMEMS DA311 3-axis 12-bit
|
||||
digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called da311.
|
||||
|
||||
config DMARD06
|
||||
tristate "Domintech DMARD06 Digital Accelerometer Driver"
|
||||
depends on OF || COMPILE_TEST
|
||||
@ -73,6 +93,16 @@ config DMARD09
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called dmard09.
|
||||
|
||||
config DMARD10
|
||||
tristate "Domintech DMARD10 3-axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Domintech DMARD10 3-axis
|
||||
accelerometer.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called dmard10.
|
||||
|
||||
config HID_SENSOR_ACCEL_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
@ -273,6 +303,18 @@ config MXC6255
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called mxc6255.
|
||||
|
||||
config SCA3000
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
depends on SPI
|
||||
tristate "VTI SCA3000 series accelerometers"
|
||||
help
|
||||
Say Y here to build support for the VTI SCA3000 series of SPI
|
||||
accelerometers. These devices use a hardware ring buffer.
|
||||
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called sca3000.
|
||||
|
||||
config STK8312
|
||||
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
|
@ -8,8 +8,11 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
|
||||
obj-$(CONFIG_DA280) += da280.o
|
||||
obj-$(CONFIG_DA311) += da311.o
|
||||
obj-$(CONFIG_DMARD06) += dmard06.o
|
||||
obj-$(CONFIG_DMARD09) += dmard09.o
|
||||
obj-$(CONFIG_DMARD10) += dmard10.o
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
@ -32,6 +35,8 @@ obj-$(CONFIG_MMA9553) += mma9553.o
|
||||
obj-$(CONFIG_MXC4005) += mxc4005.o
|
||||
obj-$(CONFIG_MXC6255) += mxc6255.o
|
||||
|
||||
obj-$(CONFIG_SCA3000) += sca3000.o
|
||||
|
||||
obj-$(CONFIG_STK8312) += stk8312.o
|
||||
obj-$(CONFIG_STK8BA50) += stk8ba50.o
|
||||
|
||||
|
183
drivers/iio/accel/da280.c
Normal file
183
drivers/iio/accel/da280.c
Normal file
@ -0,0 +1,183 @@
|
||||
/**
|
||||
* IIO driver for the MiraMEMS DA280 3-axis accelerometer and
|
||||
* IIO driver for the MiraMEMS DA226 2-axis accelerometer
|
||||
*
|
||||
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
|
||||
#define DA280_REG_CHIP_ID 0x01
|
||||
#define DA280_REG_ACC_X_LSB 0x02
|
||||
#define DA280_REG_ACC_Y_LSB 0x04
|
||||
#define DA280_REG_ACC_Z_LSB 0x06
|
||||
#define DA280_REG_MODE_BW 0x11
|
||||
|
||||
#define DA280_CHIP_ID 0x13
|
||||
#define DA280_MODE_ENABLE 0x1e
|
||||
#define DA280_MODE_DISABLE 0x9e
|
||||
|
||||
enum { da226, da280 };
|
||||
|
||||
/*
|
||||
* a value of + or -4096 corresponds to + or - 1G
|
||||
* scale = 9.81 / 4096 = 0.002395019
|
||||
*/
|
||||
|
||||
static const int da280_nscale = 2395019;
|
||||
|
||||
#define DA280_CHANNEL(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), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec da280_channels[] = {
|
||||
DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),
|
||||
DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),
|
||||
DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
|
||||
};
|
||||
|
||||
struct da280_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int da280_enable(struct i2c_client *client, bool enable)
|
||||
{
|
||||
u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);
|
||||
}
|
||||
|
||||
static int da280_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct da280_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_data(data->client, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Values are 14 bits, stored as 16 bits with the 2
|
||||
* least significant bits always 0.
|
||||
*/
|
||||
*val = (short)ret >> 2;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = da280_nscale;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info da280_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = da280_read_raw,
|
||||
};
|
||||
|
||||
static int da280_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct da280_data *data;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
|
||||
if (ret != DA280_CHIP_ID)
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &da280_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = da280_channels;
|
||||
if (id->driver_data == da226) {
|
||||
indio_dev->name = "da226";
|
||||
indio_dev->num_channels = 2;
|
||||
} else {
|
||||
indio_dev->name = "da280";
|
||||
indio_dev->num_channels = 3;
|
||||
}
|
||||
|
||||
ret = da280_enable(client, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
da280_enable(client, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da280_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return da280_enable(client, false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int da280_suspend(struct device *dev)
|
||||
{
|
||||
return da280_enable(to_i2c_client(dev), false);
|
||||
}
|
||||
|
||||
static int da280_resume(struct device *dev)
|
||||
{
|
||||
return da280_enable(to_i2c_client(dev), true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
|
||||
|
||||
static const struct i2c_device_id da280_i2c_id[] = {
|
||||
{ "da226", da226 },
|
||||
{ "da280", da280 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
|
||||
|
||||
static struct i2c_driver da280_driver = {
|
||||
.driver = {
|
||||
.name = "da280",
|
||||
.pm = &da280_pm_ops,
|
||||
},
|
||||
.probe = da280_probe,
|
||||
.remove = da280_remove,
|
||||
.id_table = da280_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(da280_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
305
drivers/iio/accel/da311.c
Normal file
305
drivers/iio/accel/da311.c
Normal file
@ -0,0 +1,305 @@
|
||||
/**
|
||||
* IIO driver for the MiraMEMS DA311 3-axis accelerometer
|
||||
*
|
||||
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
|
||||
#define DA311_CHIP_ID 0x13
|
||||
|
||||
/*
|
||||
* Note register addressed go from 0 - 0x3f and then wrap.
|
||||
* For some reason there are 2 banks with 0 - 0x3f addresses,
|
||||
* rather then a single 0-0x7f bank.
|
||||
*/
|
||||
|
||||
/* Bank 0 regs */
|
||||
#define DA311_REG_BANK 0x0000
|
||||
#define DA311_REG_LDO_REG 0x0006
|
||||
#define DA311_REG_CHIP_ID 0x000f
|
||||
#define DA311_REG_TEMP_CFG_REG 0x001f
|
||||
#define DA311_REG_CTRL_REG1 0x0020
|
||||
#define DA311_REG_CTRL_REG3 0x0022
|
||||
#define DA311_REG_CTRL_REG4 0x0023
|
||||
#define DA311_REG_CTRL_REG5 0x0024
|
||||
#define DA311_REG_CTRL_REG6 0x0025
|
||||
#define DA311_REG_STATUS_REG 0x0027
|
||||
#define DA311_REG_OUT_X_L 0x0028
|
||||
#define DA311_REG_OUT_X_H 0x0029
|
||||
#define DA311_REG_OUT_Y_L 0x002a
|
||||
#define DA311_REG_OUT_Y_H 0x002b
|
||||
#define DA311_REG_OUT_Z_L 0x002c
|
||||
#define DA311_REG_OUT_Z_H 0x002d
|
||||
#define DA311_REG_INT1_CFG 0x0030
|
||||
#define DA311_REG_INT1_SRC 0x0031
|
||||
#define DA311_REG_INT1_THS 0x0032
|
||||
#define DA311_REG_INT1_DURATION 0x0033
|
||||
#define DA311_REG_INT2_CFG 0x0034
|
||||
#define DA311_REG_INT2_SRC 0x0035
|
||||
#define DA311_REG_INT2_THS 0x0036
|
||||
#define DA311_REG_INT2_DURATION 0x0037
|
||||
#define DA311_REG_CLICK_CFG 0x0038
|
||||
#define DA311_REG_CLICK_SRC 0x0039
|
||||
#define DA311_REG_CLICK_THS 0x003a
|
||||
#define DA311_REG_TIME_LIMIT 0x003b
|
||||
#define DA311_REG_TIME_LATENCY 0x003c
|
||||
#define DA311_REG_TIME_WINDOW 0x003d
|
||||
|
||||
/* Bank 1 regs */
|
||||
#define DA311_REG_SOFT_RESET 0x0105
|
||||
#define DA311_REG_OTP_XOFF_L 0x0110
|
||||
#define DA311_REG_OTP_XOFF_H 0x0111
|
||||
#define DA311_REG_OTP_YOFF_L 0x0112
|
||||
#define DA311_REG_OTP_YOFF_H 0x0113
|
||||
#define DA311_REG_OTP_ZOFF_L 0x0114
|
||||
#define DA311_REG_OTP_ZOFF_H 0x0115
|
||||
#define DA311_REG_OTP_XSO 0x0116
|
||||
#define DA311_REG_OTP_YSO 0x0117
|
||||
#define DA311_REG_OTP_ZSO 0x0118
|
||||
#define DA311_REG_OTP_TRIM_OSC 0x011b
|
||||
#define DA311_REG_LPF_ABSOLUTE 0x011c
|
||||
#define DA311_REG_TEMP_OFF1 0x0127
|
||||
#define DA311_REG_TEMP_OFF2 0x0128
|
||||
#define DA311_REG_TEMP_OFF3 0x0129
|
||||
#define DA311_REG_OTP_TRIM_THERM_H 0x011a
|
||||
|
||||
/*
|
||||
* a value of + or -1024 corresponds to + or - 1G
|
||||
* scale = 9.81 / 1024 = 0.009580078
|
||||
*/
|
||||
|
||||
static const int da311_nscale = 9580078;
|
||||
|
||||
#define DA311_CHANNEL(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), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec da311_channels[] = {
|
||||
/* | 0x80 comes from the android driver */
|
||||
DA311_CHANNEL(DA311_REG_OUT_X_L | 0x80, X),
|
||||
DA311_CHANNEL(DA311_REG_OUT_Y_L | 0x80, Y),
|
||||
DA311_CHANNEL(DA311_REG_OUT_Z_L | 0x80, Z),
|
||||
};
|
||||
|
||||
struct da311_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int da311_register_mask_write(struct i2c_client *client, u16 addr,
|
||||
u8 mask, u8 data)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp_data = 0;
|
||||
|
||||
if (addr & 0xff00) {
|
||||
/* Select bank 1 */
|
||||
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x01);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mask != 0xff) {
|
||||
ret = i2c_smbus_read_byte_data(client, addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
tmp_data = ret;
|
||||
}
|
||||
|
||||
tmp_data &= ~mask;
|
||||
tmp_data |= data & mask;
|
||||
ret = i2c_smbus_write_byte_data(client, addr & 0xff, tmp_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (addr & 0xff00) {
|
||||
/* Back to bank 0 */
|
||||
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Init sequence taken from the android driver */
|
||||
static int da311_reset(struct i2c_client *client)
|
||||
{
|
||||
const struct {
|
||||
u16 addr;
|
||||
u8 mask;
|
||||
u8 data;
|
||||
} init_data[] = {
|
||||
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x08 },
|
||||
{ DA311_REG_CTRL_REG5, 0xff, 0x80 },
|
||||
{ DA311_REG_CTRL_REG4, 0x30, 0x00 },
|
||||
{ DA311_REG_CTRL_REG1, 0xff, 0x6f },
|
||||
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x88 },
|
||||
{ DA311_REG_LDO_REG, 0xff, 0x02 },
|
||||
{ DA311_REG_OTP_TRIM_OSC, 0xff, 0x27 },
|
||||
{ DA311_REG_LPF_ABSOLUTE, 0xff, 0x30 },
|
||||
{ DA311_REG_TEMP_OFF1, 0xff, 0x3f },
|
||||
{ DA311_REG_TEMP_OFF2, 0xff, 0xff },
|
||||
{ DA311_REG_TEMP_OFF3, 0xff, 0x0f },
|
||||
};
|
||||
int i, ret;
|
||||
|
||||
/* Reset */
|
||||
ret = da311_register_mask_write(client, DA311_REG_SOFT_RESET,
|
||||
0xff, 0xaa);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(init_data); i++) {
|
||||
ret = da311_register_mask_write(client,
|
||||
init_data[i].addr,
|
||||
init_data[i].mask,
|
||||
init_data[i].data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da311_enable(struct i2c_client *client, bool enable)
|
||||
{
|
||||
u8 data = enable ? 0x00 : 0x20;
|
||||
|
||||
return da311_register_mask_write(client, DA311_REG_TEMP_CFG_REG,
|
||||
0x20, data);
|
||||
}
|
||||
|
||||
static int da311_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct da311_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_data(data->client, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Values are 12 bits, stored as 16 bits with the 4
|
||||
* least significant bits always 0.
|
||||
*/
|
||||
*val = (short)ret >> 4;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = da311_nscale;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info da311_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = da311_read_raw,
|
||||
};
|
||||
|
||||
static int da311_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct da311_data *data;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
|
||||
if (ret != DA311_CHIP_ID)
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &da311_info;
|
||||
indio_dev->name = "da311";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = da311_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(da311_channels);
|
||||
|
||||
ret = da311_reset(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = da311_enable(client, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
da311_enable(client, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da311_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return da311_enable(client, false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int da311_suspend(struct device *dev)
|
||||
{
|
||||
return da311_enable(to_i2c_client(dev), false);
|
||||
}
|
||||
|
||||
static int da311_resume(struct device *dev)
|
||||
{
|
||||
return da311_enable(to_i2c_client(dev), true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
|
||||
|
||||
static const struct i2c_device_id da311_i2c_id[] = {
|
||||
{"da311", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
|
||||
|
||||
static struct i2c_driver da311_driver = {
|
||||
.driver = {
|
||||
.name = "da311",
|
||||
.pm = &da311_pm_ops,
|
||||
},
|
||||
.probe = da311_probe,
|
||||
.remove = da311_remove,
|
||||
.id_table = da311_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(da311_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("MiraMEMS DA311 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
266
drivers/iio/accel/dmard10.c
Normal file
266
drivers/iio/accel/dmard10.c
Normal file
@ -0,0 +1,266 @@
|
||||
/**
|
||||
* IIO driver for the 3-axis accelerometer Domintech ARD10.
|
||||
*
|
||||
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (c) 2012 Domintech Technology Co., Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
|
||||
#define DMARD10_REG_ACTR 0x00
|
||||
#define DMARD10_REG_AFEM 0x0c
|
||||
#define DMARD10_REG_STADR 0x12
|
||||
#define DMARD10_REG_STAINT 0x1c
|
||||
#define DMARD10_REG_MISC2 0x1f
|
||||
#define DMARD10_REG_PD 0x21
|
||||
|
||||
#define DMARD10_MODE_OFF 0x00
|
||||
#define DMARD10_MODE_STANDBY 0x02
|
||||
#define DMARD10_MODE_ACTIVE 0x06
|
||||
#define DMARD10_MODE_READ_OTP 0x12
|
||||
#define DMARD10_MODE_RESET_DATA_PATH 0x82
|
||||
|
||||
/* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */
|
||||
#define DMARD10_VALUE_AFEM_AFEN_NORMAL 0x8f
|
||||
/* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */
|
||||
#define DMARD10_VALUE_CKSEL_ODR_100_204 0x74
|
||||
/* INTC[6:5]=b'00 */
|
||||
#define DMARD10_VALUE_INTC 0x00
|
||||
/* TAP1/TAP2 Average 2 */
|
||||
#define DMARD10_VALUE_TAPNS_AVE_2 0x11
|
||||
|
||||
#define DMARD10_VALUE_STADR 0x55
|
||||
#define DMARD10_VALUE_STAINT 0xaa
|
||||
#define DMARD10_VALUE_MISC2_OSCA_EN 0x08
|
||||
#define DMARD10_VALUE_PD_RST 0x52
|
||||
|
||||
/* Offsets into the buffer read in dmard10_read_raw() */
|
||||
#define DMARD10_X_OFFSET 1
|
||||
#define DMARD10_Y_OFFSET 2
|
||||
#define DMARD10_Z_OFFSET 3
|
||||
|
||||
/*
|
||||
* a value of + or -128 corresponds to + or - 1G
|
||||
* scale = 9.81 / 128 = 0.076640625
|
||||
*/
|
||||
|
||||
static const int dmard10_nscale = 76640625;
|
||||
|
||||
#define DMARD10_CHANNEL(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), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec dmard10_channels[] = {
|
||||
DMARD10_CHANNEL(DMARD10_X_OFFSET, X),
|
||||
DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y),
|
||||
DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z),
|
||||
};
|
||||
|
||||
struct dmard10_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
/* Init sequence taken from the android driver */
|
||||
static int dmard10_reset(struct i2c_client *client)
|
||||
{
|
||||
unsigned char buffer[7];
|
||||
int ret;
|
||||
|
||||
/* 1. Powerdown reset */
|
||||
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD,
|
||||
DMARD10_VALUE_PD_RST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* 2. ACTR => Standby mode => Download OTP to parameter reg =>
|
||||
* Standby mode => Reset data path => Standby mode
|
||||
*/
|
||||
buffer[0] = DMARD10_REG_ACTR;
|
||||
buffer[1] = DMARD10_MODE_STANDBY;
|
||||
buffer[2] = DMARD10_MODE_READ_OTP;
|
||||
buffer[3] = DMARD10_MODE_STANDBY;
|
||||
buffer[4] = DMARD10_MODE_RESET_DATA_PATH;
|
||||
buffer[5] = DMARD10_MODE_STANDBY;
|
||||
ret = i2c_master_send(client, buffer, 6);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */
|
||||
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2,
|
||||
DMARD10_VALUE_MISC2_OSCA_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 4. AFEN = 1 (AFE will powerdown after ADC) */
|
||||
buffer[0] = DMARD10_REG_AFEM;
|
||||
buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL;
|
||||
buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204;
|
||||
buffer[3] = DMARD10_VALUE_INTC;
|
||||
buffer[4] = DMARD10_VALUE_TAPNS_AVE_2;
|
||||
buffer[5] = 0x00; /* DLYC, no delay timing */
|
||||
buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */
|
||||
ret = i2c_master_send(client, buffer, 7);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 5. Activation mode */
|
||||
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR,
|
||||
DMARD10_MODE_ACTIVE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shutdown sequence taken from the android driver */
|
||||
static int dmard10_shutdown(struct i2c_client *client)
|
||||
{
|
||||
unsigned char buffer[3];
|
||||
|
||||
buffer[0] = DMARD10_REG_ACTR;
|
||||
buffer[1] = DMARD10_MODE_STANDBY;
|
||||
buffer[2] = DMARD10_MODE_OFF;
|
||||
|
||||
return i2c_master_send(client, buffer, 3);
|
||||
}
|
||||
|
||||
static int dmard10_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dmard10_data *data = iio_priv(indio_dev);
|
||||
__le16 buf[4];
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Read 8 bytes starting at the REG_STADR register, trying to
|
||||
* read the individual X, Y, Z registers will always read 0.
|
||||
*/
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
DMARD10_REG_STADR,
|
||||
sizeof(buf), (u8 *)buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = le16_to_cpu(buf[chan->address]);
|
||||
*val = sign_extend32(ret, 12);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = dmard10_nscale;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info dmard10_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = dmard10_read_raw,
|
||||
};
|
||||
|
||||
static int dmard10_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct dmard10_data *data;
|
||||
|
||||
/* These 2 registers have special POR reset values used for id */
|
||||
ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
|
||||
if (ret != DMARD10_VALUE_STADR)
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
|
||||
if (ret != DMARD10_VALUE_STAINT)
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &dmard10_info;
|
||||
indio_dev->name = "dmard10";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dmard10_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(dmard10_channels);
|
||||
|
||||
ret = dmard10_reset(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
dmard10_shutdown(client);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dmard10_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return dmard10_shutdown(client);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dmard10_suspend(struct device *dev)
|
||||
{
|
||||
return dmard10_shutdown(to_i2c_client(dev));
|
||||
}
|
||||
|
||||
static int dmard10_resume(struct device *dev)
|
||||
{
|
||||
return dmard10_reset(to_i2c_client(dev));
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
|
||||
|
||||
static const struct i2c_device_id dmard10_i2c_id[] = {
|
||||
{"dmard10", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
|
||||
|
||||
static struct i2c_driver dmard10_driver = {
|
||||
.driver = {
|
||||
.name = "dmard10",
|
||||
.pm = &dmard10_pm_ops,
|
||||
},
|
||||
.probe = dmard10_probe,
|
||||
.remove = dmard10_remove,
|
||||
.id_table = dmard10_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(dmard10_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -39,7 +39,7 @@
|
||||
|
||||
#define MMA7660_SCALE_AVAIL "0.467142857"
|
||||
|
||||
const int mma7660_nscale = 467142857;
|
||||
static const int mma7660_nscale = 467142857;
|
||||
|
||||
#define MMA7660_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
|
@ -459,12 +459,14 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mma8452_read(data, buffer);
|
||||
mutex_unlock(&data->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -664,37 +666,46 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = mma8452_get_samp_freq_index(data, val, val2);
|
||||
if (i < 0)
|
||||
return i;
|
||||
|
||||
if (i < 0) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
i = mma8452_get_scale_index(data, val, val2);
|
||||
if (i < 0)
|
||||
return i;
|
||||
if (i < 0) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
|
||||
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
|
||||
data->data_cfg |= i;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -128 || val > 127)
|
||||
return -EINVAL;
|
||||
if (val < -128 || val > 127) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return mma8452_change_config(data,
|
||||
MMA8452_OFF_X + chan->scan_index,
|
||||
val);
|
||||
ret = mma8452_change_config(data,
|
||||
MMA8452_OFF_X + chan->scan_index,
|
||||
val);
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||
if (val == 0 && val2 == 0) {
|
||||
@ -703,23 +714,30 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
|
||||
ret = mma8452_set_hp_filter_frequency(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = mma8452_get_odr_index(data);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
|
||||
if (mma8452_os_ratio[i][ret] == val)
|
||||
return mma8452_set_power_mode(data, i);
|
||||
if (mma8452_os_ratio[i][ret] == val) {
|
||||
ret = mma8452_set_power_mode(data, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
||||
@ -1347,20 +1365,9 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
|
||||
}
|
||||
|
||||
static int mma8452_validate_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (indio != indio_dev)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops mma8452_trigger_ops = {
|
||||
.set_trigger_state = mma8452_data_rdy_trigger_set_state,
|
||||
.validate_device = mma8452_validate_device,
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
1576
drivers/iio/accel/sca3000.c
Normal file
1576
drivers/iio/accel/sca3000.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -58,6 +58,18 @@ config AD7476
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7476.
|
||||
|
||||
config AD7766
|
||||
tristate "Analog Devices AD7766/AD7767 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7766, AD7766-1,
|
||||
AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7766.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
@ -447,6 +459,8 @@ config TI_ADC081C
|
||||
config TI_ADC0832
|
||||
tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC0831,
|
||||
ADC0832, ADC0834, ADC0838 ADC chips.
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7766) += ad7766.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
|
330
drivers/iio/adc/ad7766.c
Normal file
330
drivers/iio/adc/ad7766.c
Normal file
@ -0,0 +1,330 @@
|
||||
/*
|
||||
* AD7766/AD7767 SPI ADC driver
|
||||
*
|
||||
* Copyright 2016 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
struct ad7766_chip_info {
|
||||
unsigned int decimation_factor;
|
||||
};
|
||||
|
||||
enum {
|
||||
AD7766_SUPPLY_AVDD = 0,
|
||||
AD7766_SUPPLY_DVDD = 1,
|
||||
AD7766_SUPPLY_VREF = 2,
|
||||
AD7766_NUM_SUPPLIES = 3
|
||||
};
|
||||
|
||||
struct ad7766 {
|
||||
const struct ad7766_chip_info *chip_info;
|
||||
struct spi_device *spi;
|
||||
struct clk *mclk;
|
||||
struct gpio_desc *pd_gpio;
|
||||
struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES];
|
||||
|
||||
struct iio_trigger *trig;
|
||||
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* Make the buffer large enough for one 24 bit sample and one 64 bit
|
||||
* aligned 64 bit timestamp.
|
||||
*/
|
||||
unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)]
|
||||
____cacheline_aligned;
|
||||
};
|
||||
|
||||
/*
|
||||
* AD7766 and AD7767 variations are interface compatible, the main difference is
|
||||
* analog performance. Both parts will use the same ID.
|
||||
*/
|
||||
enum ad7766_device_ids {
|
||||
ID_AD7766,
|
||||
ID_AD7766_1,
|
||||
ID_AD7766_2,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7766_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7766 *ad7766 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(ad7766->spi, &ad7766->msg);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data,
|
||||
pf->timestamp);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7766_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7766 *ad7766 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(ad7766->mclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ad7766->pd_gpio)
|
||||
gpiod_set_value(ad7766->pd_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7766_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7766 *ad7766 = iio_priv(indio_dev);
|
||||
|
||||
if (ad7766->pd_gpio)
|
||||
gpiod_set_value(ad7766->pd_gpio, 1);
|
||||
|
||||
/*
|
||||
* The PD pin is synchronous to the clock, so give it some time to
|
||||
* notice the change before we disable the clock.
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
clk_disable_unprepare(ad7766->mclk);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7766_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7766 *ad7766 = iio_priv(indio_dev);
|
||||
struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer;
|
||||
int scale_uv;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = regulator_get_voltage(vref);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = clk_get_rate(ad7766->mclk) /
|
||||
ad7766->chip_info->decimation_factor;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7766_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7766_chip_info ad7766_chip_info[] = {
|
||||
[ID_AD7766] = {
|
||||
.decimation_factor = 8,
|
||||
},
|
||||
[ID_AD7766_1] = {
|
||||
.decimation_factor = 16,
|
||||
},
|
||||
[ID_AD7766_2] = {
|
||||
.decimation_factor = 32,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = {
|
||||
.preenable = &ad7766_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad7766_postdisable,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7766_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7766_read_raw,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7766_irq(int irq, void *private)
|
||||
{
|
||||
iio_trigger_poll(private);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
|
||||
{
|
||||
struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (enable)
|
||||
enable_irq(ad7766->spi->irq);
|
||||
else
|
||||
disable_irq(ad7766->spi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops ad7766_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = ad7766_set_trigger_state,
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
};
|
||||
|
||||
static int ad7766_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7766 *ad7766;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ad7766 = iio_priv(indio_dev);
|
||||
ad7766->chip_info = &ad7766_chip_info[id->driver_data];
|
||||
|
||||
ad7766->mclk = devm_clk_get(&spi->dev, "mclk");
|
||||
if (IS_ERR(ad7766->mclk))
|
||||
return PTR_ERR(ad7766->mclk);
|
||||
|
||||
ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd";
|
||||
ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd";
|
||||
ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref";
|
||||
|
||||
ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg),
|
||||
ad7766->reg);
|
||||
if (IS_ERR(ad7766->reg))
|
||||
return PTR_ERR(ad7766->reg);
|
||||
|
||||
ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ad7766->pd_gpio))
|
||||
return PTR_ERR(ad7766->pd_gpio);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad7766_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7766_channels);
|
||||
indio_dev->info = &ad7766_info;
|
||||
|
||||
if (spi->irq > 0) {
|
||||
ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
if (!ad7766->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
ad7766->trig->ops = &ad7766_trigger_ops;
|
||||
ad7766->trig->dev.parent = &spi->dev;
|
||||
iio_trigger_set_drvdata(ad7766->trig, ad7766);
|
||||
|
||||
ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
|
||||
IRQF_TRIGGER_FALLING, dev_name(&spi->dev),
|
||||
ad7766->trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The device generates interrupts as long as it is powered up.
|
||||
* Some platforms might not allow the option to power it down so
|
||||
* disable the interrupt to avoid extra load on the system
|
||||
*/
|
||||
disable_irq(spi->irq);
|
||||
|
||||
ret = devm_iio_trigger_register(&spi->dev, ad7766->trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ad7766->spi = spi;
|
||||
|
||||
/* First byte always 0 */
|
||||
ad7766->xfer.rx_buf = &ad7766->data[1];
|
||||
ad7766->xfer.len = 3;
|
||||
|
||||
spi_message_init(&ad7766->msg);
|
||||
spi_message_add_tail(&ad7766->xfer, &ad7766->msg);
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
|
||||
&iio_pollfunc_store_time, &ad7766_trigger_handler,
|
||||
&ad7766_buffer_setup_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7766_id[] = {
|
||||
{"ad7766", ID_AD7766},
|
||||
{"ad7766-1", ID_AD7766_1},
|
||||
{"ad7766-2", ID_AD7766_2},
|
||||
{"ad7767", ID_AD7766},
|
||||
{"ad7767-1", ID_AD7766_1},
|
||||
{"ad7767-2", ID_AD7766_2},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7766_id);
|
||||
|
||||
static struct spi_driver ad7766_driver = {
|
||||
.driver = {
|
||||
.name = "ad7766",
|
||||
},
|
||||
.probe = ad7766_probe,
|
||||
.id_table = ad7766_id,
|
||||
};
|
||||
module_spi_driver(ad7766_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -238,7 +238,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
|
||||
|
||||
/* Configure conversion register with the requested chan */
|
||||
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
|
||||
MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
|
||||
MAX1027_NOSCAN;
|
||||
if (chan->type == IIO_TEMP)
|
||||
st->reg |= MAX1027_TEMP;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
@ -360,17 +362,6 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max1027_validate_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (indio != indio_dev)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t max1027_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = (struct iio_poll_func *)private;
|
||||
@ -391,7 +382,7 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private)
|
||||
|
||||
static const struct iio_trigger_ops max1027_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.validate_device = &max1027_validate_device,
|
||||
.validate_device = &iio_trigger_validate_own_device,
|
||||
.set_trigger_state = &max1027_set_trigger_state,
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,10 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
enum {
|
||||
adc0831,
|
||||
@ -38,10 +42,16 @@ struct adc0832 {
|
||||
.indexed = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = chan, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 8, \
|
||||
.storagebits = 8, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
|
||||
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
@ -49,18 +59,26 @@ struct adc0832 {
|
||||
.channel2 = (chan2), \
|
||||
.differential = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = si, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 8, \
|
||||
.storagebits = 8, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc0831_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc0832_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL(0),
|
||||
ADC0832_VOLTAGE_CHANNEL(1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc0834_channels[] = {
|
||||
@ -68,10 +86,11 @@ static const struct iio_chan_spec adc0834_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL(1),
|
||||
ADC0832_VOLTAGE_CHANNEL(2),
|
||||
ADC0832_VOLTAGE_CHANNEL(3),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc0838_channels[] = {
|
||||
@ -83,14 +102,15 @@ static const struct iio_chan_spec adc0838_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL(5),
|
||||
ADC0832_VOLTAGE_CHANNEL(6),
|
||||
ADC0832_VOLTAGE_CHANNEL(7),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16),
|
||||
};
|
||||
|
||||
static int adc0831_adc_conversion(struct adc0832 *adc)
|
||||
@ -178,6 +198,42 @@ static const struct iio_info adc0832_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t adc0832_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adc0832 *adc = iio_priv(indio_dev);
|
||||
u8 data[24] = { }; /* 16x 1 byte ADC data + 8 bytes timestamp */
|
||||
int scan_index;
|
||||
int i = 0;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
const struct iio_chan_spec *scan_chan =
|
||||
&indio_dev->channels[scan_index];
|
||||
int ret = adc0832_adc_conversion(adc, scan_chan->channel,
|
||||
scan_chan->differential);
|
||||
if (ret < 0) {
|
||||
dev_warn(&adc->spi->dev,
|
||||
"failed to get conversion data\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
data[i] = ret;
|
||||
i++;
|
||||
}
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc0832_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -233,9 +289,20 @@ static int adc0832_probe(struct spi_device *spi)
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
adc0832_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto err_reg_disable;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
regulator_disable(adc->reg);
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_reg_disable:
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -246,6 +313,7 @@ static int adc0832_remove(struct spi_device *spi)
|
||||
struct adc0832 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define TI_ADC_DRV_NAME "ti-adc161s626"
|
||||
|
||||
@ -39,7 +40,9 @@ static const struct iio_chan_spec ti_adc141s626_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
@ -54,7 +57,9 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
@ -68,6 +73,8 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
|
||||
struct ti_adc_data {
|
||||
struct iio_dev *indio_dev;
|
||||
struct spi_device *spi;
|
||||
struct regulator *ref;
|
||||
|
||||
u8 read_size;
|
||||
u8 shift;
|
||||
|
||||
@ -135,18 +142,32 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct ti_adc_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ti_adc_read_measurement(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
ret = ti_adc_read_measurement(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ret)
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(data->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 1 << (chan->scan_type.realbits - 1);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -191,10 +212,17 @@ static int ti_adc_probe(struct spi_device *spi)
|
||||
break;
|
||||
}
|
||||
|
||||
data->ref = devm_regulator_get(&spi->dev, "vdda");
|
||||
if (!IS_ERR(data->ref)) {
|
||||
ret = regulator_enable(data->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ti_adc_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto error_regulator_disable;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
@ -205,15 +233,20 @@ static int ti_adc_probe(struct spi_device *spi)
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
error_regulator_disable:
|
||||
regulator_disable(data->ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_adc_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ti_adc_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(data->ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
593
drivers/iio/counter/104-quad-8.c
Normal file
593
drivers/iio/counter/104-quad-8.c
Normal file
@ -0,0 +1,593 @@
|
||||
/*
|
||||
* IIO driver for the ACCES 104-QUAD-8
|
||||
* Copyright (C) 2016 William Breathitt Gray
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define QUAD8_EXTENT 32
|
||||
|
||||
static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
|
||||
static unsigned int num_quad8;
|
||||
module_param_array(base, uint, &num_quad8, 0);
|
||||
MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
|
||||
|
||||
#define QUAD8_NUM_COUNTERS 8
|
||||
|
||||
/**
|
||||
* struct quad8_iio - IIO device private data structure
|
||||
* @preset: array of preset values
|
||||
* @count_mode: array of count mode configurations
|
||||
* @quadrature_mode: array of quadrature mode configurations
|
||||
* @quadrature_scale: array of quadrature mode scale configurations
|
||||
* @ab_enable: array of A and B inputs enable configurations
|
||||
* @preset_enable: array of set_to_preset_on_index attribute configurations
|
||||
* @synchronous_mode: array of index function synchronous mode configurations
|
||||
* @index_polarity: array of index function polarity configurations
|
||||
* @base: base port address of the IIO device
|
||||
*/
|
||||
struct quad8_iio {
|
||||
unsigned int preset[QUAD8_NUM_COUNTERS];
|
||||
unsigned int count_mode[QUAD8_NUM_COUNTERS];
|
||||
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
|
||||
unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
|
||||
unsigned int ab_enable[QUAD8_NUM_COUNTERS];
|
||||
unsigned int preset_enable[QUAD8_NUM_COUNTERS];
|
||||
unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
|
||||
unsigned int index_polarity[QUAD8_NUM_COUNTERS];
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
static int quad8_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel;
|
||||
unsigned int flags;
|
||||
unsigned int borrow;
|
||||
unsigned int carry;
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_INDEX) {
|
||||
*val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
flags = inb(base_offset);
|
||||
borrow = flags & BIT(0);
|
||||
carry = !!(flags & BIT(1));
|
||||
|
||||
/* Borrow XOR Carry effectively doubles count range */
|
||||
*val = (borrow ^ carry) << 24;
|
||||
|
||||
/* Reset Byte Pointer; transfer Counter to Output Latch */
|
||||
outb(0x11, base_offset + 1);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
*val |= (unsigned int)inb(base_offset) << (8 * i);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
*val = priv->ab_enable[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1;
|
||||
*val2 = priv->quadrature_scale[chan->channel];
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int quad8_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel;
|
||||
int i;
|
||||
unsigned int ior_cfg;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_INDEX)
|
||||
return -EINVAL;
|
||||
|
||||
/* Only 24-bit values are supported */
|
||||
if ((unsigned int)val > 0xFFFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
|
||||
/* Counter can only be set via Preset Register */
|
||||
for (i = 0; i < 3; i++)
|
||||
outb(val >> (8 * i), base_offset);
|
||||
|
||||
/* Transfer Preset Register to Counter */
|
||||
outb(0x08, base_offset + 1);
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
|
||||
/* Set Preset Register back to original value */
|
||||
val = priv->preset[chan->channel];
|
||||
for (i = 0; i < 3; i++)
|
||||
outb(val >> (8 * i), base_offset);
|
||||
|
||||
/* Reset Borrow, Carry, Compare, and Sign flags */
|
||||
outb(0x02, base_offset + 1);
|
||||
/* Reset Error flag */
|
||||
outb(0x06, base_offset + 1);
|
||||
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
/* only boolean values accepted */
|
||||
if (val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
priv->ab_enable[chan->channel] = val;
|
||||
|
||||
ior_cfg = val | priv->preset_enable[chan->channel] << 1;
|
||||
|
||||
/* Load I/O control configuration */
|
||||
outb(0x40 | ior_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Quadrature scaling only available in quadrature mode */
|
||||
if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
|
||||
return -EINVAL;
|
||||
|
||||
/* Only three gain states (1, 0.5, 0.25) */
|
||||
if (val == 1 && !val2)
|
||||
priv->quadrature_scale[chan->channel] = 0;
|
||||
else if (!val)
|
||||
switch (val2) {
|
||||
case 500000:
|
||||
priv->quadrature_scale[chan->channel] = 1;
|
||||
break;
|
||||
case 250000:
|
||||
priv->quadrature_scale[chan->channel] = 2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info quad8_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = quad8_read_raw,
|
||||
.write_raw = quad8_write_raw
|
||||
};
|
||||
|
||||
static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
const struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
|
||||
}
|
||||
|
||||
static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
|
||||
const struct iio_chan_spec *chan, const char *buf, size_t len)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel;
|
||||
unsigned int preset;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = kstrtouint(buf, 0, &preset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Only 24-bit values are supported */
|
||||
if (preset > 0xFFFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
priv->preset[chan->channel] = preset;
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
|
||||
/* Set Preset Register */
|
||||
for (i = 0; i < 3; i++)
|
||||
outb(preset >> (8 * i), base_offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
const struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
priv->preset_enable[chan->channel]);
|
||||
}
|
||||
|
||||
static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel;
|
||||
bool preset_enable;
|
||||
int ret;
|
||||
unsigned int ior_cfg;
|
||||
|
||||
ret = kstrtobool(buf, &preset_enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->preset_enable[chan->channel] = preset_enable;
|
||||
|
||||
ior_cfg = priv->ab_enable[chan->channel] |
|
||||
(unsigned int)preset_enable << 1;
|
||||
|
||||
/* Load I/O control configuration to Input / Output Control Register */
|
||||
outb(0x40 | ior_cfg, base_offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const char *const quad8_noise_error_states[] = {
|
||||
"No excessive noise is present at the count inputs",
|
||||
"Excessive noise is present at the count inputs"
|
||||
};
|
||||
|
||||
static int quad8_get_noise_error(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
return !!(inb(base_offset) & BIT(4));
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_noise_error_enum = {
|
||||
.items = quad8_noise_error_states,
|
||||
.num_items = ARRAY_SIZE(quad8_noise_error_states),
|
||||
.get = quad8_get_noise_error
|
||||
};
|
||||
|
||||
static const char *const quad8_count_direction_states[] = {
|
||||
"down",
|
||||
"up"
|
||||
};
|
||||
|
||||
static int quad8_get_count_direction(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
return !!(inb(base_offset) & BIT(5));
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_count_direction_enum = {
|
||||
.items = quad8_count_direction_states,
|
||||
.num_items = ARRAY_SIZE(quad8_count_direction_states),
|
||||
.get = quad8_get_count_direction
|
||||
};
|
||||
|
||||
static const char *const quad8_count_modes[] = {
|
||||
"normal",
|
||||
"range limit",
|
||||
"non-recycle",
|
||||
"modulo-n"
|
||||
};
|
||||
|
||||
static int quad8_set_count_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int count_mode)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
unsigned int mode_cfg = count_mode << 1;
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
priv->count_mode[chan->channel] = count_mode;
|
||||
|
||||
/* Add quadrature mode configuration */
|
||||
if (priv->quadrature_mode[chan->channel])
|
||||
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
|
||||
|
||||
/* Load mode configuration to Counter Mode Register */
|
||||
outb(0x20 | mode_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quad8_get_count_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
const struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->count_mode[chan->channel];
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_count_mode_enum = {
|
||||
.items = quad8_count_modes,
|
||||
.num_items = ARRAY_SIZE(quad8_count_modes),
|
||||
.set = quad8_set_count_mode,
|
||||
.get = quad8_get_count_mode
|
||||
};
|
||||
|
||||
static const char *const quad8_synchronous_modes[] = {
|
||||
"non-synchronous",
|
||||
"synchronous"
|
||||
};
|
||||
|
||||
static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int synchronous_mode)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const unsigned int idr_cfg = synchronous_mode |
|
||||
priv->index_polarity[chan->channel] << 1;
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
/* Index function must be non-synchronous in non-quadrature mode */
|
||||
if (synchronous_mode && !priv->quadrature_mode[chan->channel])
|
||||
return -EINVAL;
|
||||
|
||||
priv->synchronous_mode[chan->channel] = synchronous_mode;
|
||||
|
||||
/* Load Index Control configuration to Index Control Register */
|
||||
outb(0x40 | idr_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
const struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->synchronous_mode[chan->channel];
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_synchronous_mode_enum = {
|
||||
.items = quad8_synchronous_modes,
|
||||
.num_items = ARRAY_SIZE(quad8_synchronous_modes),
|
||||
.set = quad8_set_synchronous_mode,
|
||||
.get = quad8_get_synchronous_mode
|
||||
};
|
||||
|
||||
static const char *const quad8_quadrature_modes[] = {
|
||||
"non-quadrature",
|
||||
"quadrature"
|
||||
};
|
||||
|
||||
static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int quadrature_mode)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
if (quadrature_mode)
|
||||
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
|
||||
else {
|
||||
/* Quadrature scaling only available in quadrature mode */
|
||||
priv->quadrature_scale[chan->channel] = 0;
|
||||
|
||||
/* Synchronous function not supported in non-quadrature mode */
|
||||
if (priv->synchronous_mode[chan->channel])
|
||||
quad8_set_synchronous_mode(indio_dev, chan, 0);
|
||||
}
|
||||
|
||||
priv->quadrature_mode[chan->channel] = quadrature_mode;
|
||||
|
||||
/* Load mode configuration to Counter Mode Register */
|
||||
outb(0x20 | mode_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
const struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->quadrature_mode[chan->channel];
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_quadrature_mode_enum = {
|
||||
.items = quad8_quadrature_modes,
|
||||
.num_items = ARRAY_SIZE(quad8_quadrature_modes),
|
||||
.set = quad8_set_quadrature_mode,
|
||||
.get = quad8_get_quadrature_mode
|
||||
};
|
||||
|
||||
static const char *const quad8_index_polarity_modes[] = {
|
||||
"negative",
|
||||
"positive"
|
||||
};
|
||||
|
||||
static int quad8_set_index_polarity(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int index_polarity)
|
||||
{
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
|
||||
index_polarity << 1;
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
priv->index_polarity[chan->channel] = index_polarity;
|
||||
|
||||
/* Load Index Control configuration to Index Control Register */
|
||||
outb(0x40 | idr_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quad8_get_index_polarity(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
const struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->index_polarity[chan->channel];
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_index_polarity_enum = {
|
||||
.items = quad8_index_polarity_modes,
|
||||
.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
|
||||
.set = quad8_set_index_polarity,
|
||||
.get = quad8_get_index_polarity
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
|
||||
{
|
||||
.name = "preset",
|
||||
.shared = IIO_SEPARATE,
|
||||
.read = quad8_read_preset,
|
||||
.write = quad8_write_preset
|
||||
},
|
||||
{
|
||||
.name = "set_to_preset_on_index",
|
||||
.shared = IIO_SEPARATE,
|
||||
.read = quad8_read_set_to_preset_on_index,
|
||||
.write = quad8_write_set_to_preset_on_index
|
||||
},
|
||||
IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
|
||||
IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
|
||||
IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
|
||||
IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
|
||||
IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
|
||||
IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
|
||||
IIO_ENUM("synchronous_mode", IIO_SEPARATE,
|
||||
&quad8_synchronous_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
|
||||
IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
|
||||
IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
|
||||
{}
|
||||
};
|
||||
|
||||
#define QUAD8_COUNT_CHAN(_chan) { \
|
||||
.type = IIO_COUNT, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = quad8_count_ext_info, \
|
||||
.indexed = 1 \
|
||||
}
|
||||
|
||||
#define QUAD8_INDEX_CHAN(_chan) { \
|
||||
.type = IIO_INDEX, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.ext_info = quad8_index_ext_info, \
|
||||
.indexed = 1 \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec quad8_channels[] = {
|
||||
QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
|
||||
QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
|
||||
QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
|
||||
QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
|
||||
QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
|
||||
QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
|
||||
QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
|
||||
QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
|
||||
};
|
||||
|
||||
static int quad8_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct quad8_iio *priv;
|
||||
int i, j;
|
||||
unsigned int base_offset;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
|
||||
dev_name(dev))) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
base[id], base[id] + QUAD8_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
indio_dev->info = &quad8_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
|
||||
indio_dev->channels = quad8_channels;
|
||||
indio_dev->name = dev_name(dev);
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
||||
/* Reset all counters and disable interrupt function */
|
||||
outb(0x01, base[id] + 0x11);
|
||||
/* Set initial configuration for all counters */
|
||||
for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
|
||||
base_offset = base[id] + 2 * i;
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
/* Reset Preset Register */
|
||||
for (j = 0; j < 3; j++)
|
||||
outb(0x00, base_offset);
|
||||
/* Reset Borrow, Carry, Compare, and Sign flags */
|
||||
outb(0x04, base_offset + 1);
|
||||
/* Reset Error flag */
|
||||
outb(0x06, base_offset + 1);
|
||||
/* Binary encoding; Normal count; non-quadrature mode */
|
||||
outb(0x20, base_offset + 1);
|
||||
/* Disable A and B inputs; preset on index; FLG1 as Carry */
|
||||
outb(0x40, base_offset + 1);
|
||||
/* Disable index function; negative index polarity */
|
||||
outb(0x60, base_offset + 1);
|
||||
}
|
||||
/* Enable all counters */
|
||||
outb(0x00, base[id] + 0x11);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct isa_driver quad8_driver = {
|
||||
.probe = quad8_probe,
|
||||
.driver = {
|
||||
.name = "104-quad-8"
|
||||
}
|
||||
};
|
||||
|
||||
module_isa_driver(quad8_driver, num_quad8);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
24
drivers/iio/counter/Kconfig
Normal file
24
drivers/iio/counter/Kconfig
Normal file
@ -0,0 +1,24 @@
|
||||
#
|
||||
# Counter devices
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Counters"
|
||||
|
||||
config 104_QUAD_8
|
||||
tristate "ACCES 104-QUAD-8 driver"
|
||||
depends on X86 && ISA_BUS_API
|
||||
help
|
||||
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
|
||||
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
|
||||
|
||||
Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
|
||||
also clears the counter's respective error flag. Although the counters
|
||||
have a 25-bit range, only the lower 24 bits may be set, either directly
|
||||
or via a counter's preset attribute. Interrupts are not supported by
|
||||
this driver.
|
||||
|
||||
The base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
endmenu
|
7
drivers/iio/counter/Makefile
Normal file
7
drivers/iio/counter/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for IIO counter devices
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
|
@ -17,7 +17,7 @@
|
||||
#define AD5592R_GPIO_READBACK_EN BIT(10)
|
||||
#define AD5592R_LDAC_READBACK_EN BIT(6)
|
||||
|
||||
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
|
||||
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
|
||||
{
|
||||
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
|
||||
struct spi_transfer t = {
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -26,12 +28,20 @@
|
||||
|
||||
#define MCP4725_DRV_NAME "mcp4725"
|
||||
|
||||
#define MCP472X_REF_VDD 0x00
|
||||
#define MCP472X_REF_VREF_UNBUFFERED 0x02
|
||||
#define MCP472X_REF_VREF_BUFFERED 0x03
|
||||
|
||||
struct mcp4725_data {
|
||||
struct i2c_client *client;
|
||||
u16 vref_mv;
|
||||
int id;
|
||||
unsigned ref_mode;
|
||||
bool vref_buffered;
|
||||
u16 dac_value;
|
||||
bool powerdown;
|
||||
unsigned powerdown_mode;
|
||||
struct regulator *vdd_reg;
|
||||
struct regulator *vref_reg;
|
||||
};
|
||||
|
||||
static int mcp4725_suspend(struct device *dev)
|
||||
@ -86,6 +96,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
|
||||
return 0;
|
||||
|
||||
inoutbuf[0] = 0x60; /* write EEPROM */
|
||||
inoutbuf[0] |= data->ref_mode << 3;
|
||||
inoutbuf[1] = data->dac_value >> 4;
|
||||
inoutbuf[2] = (data->dac_value & 0xf) << 4;
|
||||
|
||||
@ -278,18 +289,49 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp4726_set_cfg(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
u8 outbuf[3];
|
||||
int ret;
|
||||
|
||||
outbuf[0] = 0x40;
|
||||
outbuf[0] |= data->ref_mode << 3;
|
||||
if (data->powerdown)
|
||||
outbuf[0] |= data->powerdown << 1;
|
||||
outbuf[1] = data->dac_value >> 4;
|
||||
outbuf[2] = (data->dac_value & 0xf) << 4;
|
||||
|
||||
ret = i2c_master_send(data->client, outbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp4725_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->dac_value;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = data->vref_mv;
|
||||
if (data->ref_mode == MCP472X_REF_VDD)
|
||||
ret = regulator_get_voltage(data->vdd_reg);
|
||||
else
|
||||
ret = regulator_get_voltage(data->vref_reg);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
@ -323,27 +365,98 @@ static const struct iio_info mcp4725_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int mcp4725_probe_dt(struct device *dev,
|
||||
struct mcp4725_platform_data *pdata)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
/* check if is the vref-supply defined */
|
||||
pdata->use_vref = of_property_read_bool(np, "vref-supply");
|
||||
pdata->vref_buffered =
|
||||
of_property_read_bool(np, "microchip,vref-buffered");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int mcp4725_probe_dt(struct device *dev,
|
||||
struct mcp4725_platform_data *platform_data)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mcp4725_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mcp4725_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp4725_platform_data *platform_data = client->dev.platform_data;
|
||||
u8 inbuf[3];
|
||||
struct mcp4725_platform_data *pdata, pdata_dt;
|
||||
u8 inbuf[4];
|
||||
u8 pd;
|
||||
u8 ref;
|
||||
int err;
|
||||
|
||||
if (!platform_data || !platform_data->vref_mv) {
|
||||
dev_err(&client->dev, "invalid platform data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->id = id->driver_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (!pdata) {
|
||||
err = mcp4725_probe_dt(&client->dev, &pdata_dt);
|
||||
if (err) {
|
||||
dev_err(&client->dev,
|
||||
"invalid platform or devicetree data");
|
||||
return err;
|
||||
}
|
||||
pdata = &pdata_dt;
|
||||
}
|
||||
|
||||
if (data->id == MCP4725 && pdata->use_vref) {
|
||||
dev_err(&client->dev,
|
||||
"external reference is unavailable on MCP4725");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->use_vref && pdata->vref_buffered) {
|
||||
dev_err(&client->dev,
|
||||
"buffering is unavailable on the internal reference");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->use_vref)
|
||||
data->ref_mode = MCP472X_REF_VDD;
|
||||
else
|
||||
data->ref_mode = pdata->vref_buffered ?
|
||||
MCP472X_REF_VREF_BUFFERED :
|
||||
MCP472X_REF_VREF_UNBUFFERED;
|
||||
|
||||
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(data->vdd_reg))
|
||||
return PTR_ERR(data->vdd_reg);
|
||||
|
||||
err = regulator_enable(data->vdd_reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pdata->use_vref) {
|
||||
data->vref_reg = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(data->vref_reg)) {
|
||||
err = PTR_ERR(data->vref_reg);
|
||||
goto err_disable_vdd_reg;
|
||||
}
|
||||
|
||||
err = regulator_enable(data->vref_reg);
|
||||
if (err)
|
||||
goto err_disable_vdd_reg;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
@ -352,25 +465,56 @@ static int mcp4725_probe(struct i2c_client *client,
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->vref_mv = platform_data->vref_mv;
|
||||
/* read current DAC value and settings */
|
||||
err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
|
||||
|
||||
/* read current DAC value */
|
||||
err = i2c_master_recv(client, inbuf, 3);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "failed to read DAC value");
|
||||
return err;
|
||||
goto err_disable_vref_reg;
|
||||
}
|
||||
pd = (inbuf[0] >> 1) & 0x3;
|
||||
data->powerdown = pd > 0 ? true : false;
|
||||
data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
|
||||
data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
|
||||
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
|
||||
if (data->id == MCP4726)
|
||||
ref = (inbuf[3] >> 3) & 0x3;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
if (data->id == MCP4726 && ref != data->ref_mode) {
|
||||
dev_info(&client->dev,
|
||||
"voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
|
||||
data->ref_mode, ref, data->ref_mode);
|
||||
err = mcp4726_set_cfg(indio_dev);
|
||||
if (err < 0)
|
||||
goto err_disable_vref_reg;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto err_disable_vref_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_vref_reg:
|
||||
if (data->vref_reg)
|
||||
regulator_disable(data->vref_reg);
|
||||
|
||||
err_disable_vdd_reg:
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mcp4725_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (data->vref_reg)
|
||||
regulator_disable(data->vref_reg);
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@ config DHT11
|
||||
config HDC100X
|
||||
tristate "TI HDC100x relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Texas Instruments
|
||||
HDC1000 and HDC1008 relative humidity and temperature sensors.
|
||||
@ -34,6 +36,28 @@ config HDC100X
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
||||
config HTS221
|
||||
tristate "STMicroelectronics HTS221 sensor Driver"
|
||||
depends on (I2C || SPI)
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HTS221_I2C if (I2C)
|
||||
select HTS221_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics HTS221
|
||||
temperature-humidity sensor
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hts221.
|
||||
|
||||
config HTS221_I2C
|
||||
tristate
|
||||
depends on HTS221
|
||||
|
||||
config HTS221_SPI
|
||||
tristate
|
||||
depends on HTS221
|
||||
|
||||
config HTU21
|
||||
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
|
||||
depends on I2C
|
||||
|
@ -5,6 +5,13 @@
|
||||
obj-$(CONFIG_AM2315) += am2315.o
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_HDC100X) += hdc100x.o
|
||||
|
||||
hts221-y := hts221_core.o \
|
||||
hts221_buffer.o
|
||||
obj-$(CONFIG_HTS221) += hts221.o
|
||||
obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
|
||||
obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
|
||||
|
||||
obj-$(CONFIG_HTU21) += htu21.o
|
||||
obj-$(CONFIG_SI7005) += si7005.o
|
||||
obj-$(CONFIG_SI7020) += si7020.o
|
||||
|
@ -22,11 +22,15 @@
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define HDC100X_REG_TEMP 0x00
|
||||
#define HDC100X_REG_HUMIDITY 0x01
|
||||
|
||||
#define HDC100X_REG_CONFIG 0x02
|
||||
#define HDC100X_REG_CONFIG_ACQ_MODE BIT(12)
|
||||
#define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
|
||||
|
||||
struct hdc100x_data {
|
||||
@ -87,22 +91,40 @@ static const struct iio_chan_spec hdc100x_channels[] = {
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.address = HDC100X_REG_HUMIDITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME)
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.extend_name = "heater",
|
||||
.output = 1,
|
||||
.scan_index = -1,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static const unsigned long hdc100x_scan_masks[] = {0x3, 0};
|
||||
|
||||
static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
|
||||
{
|
||||
int tmp = (~mask & data->config) | val;
|
||||
@ -183,7 +205,14 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
|
||||
*val = hdc100x_get_heater_status(data);
|
||||
ret = IIO_VAL_INT;
|
||||
} else {
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hdc100x_get_measurement(data, chan);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret >= 0) {
|
||||
*val = ret;
|
||||
ret = IIO_VAL_INT;
|
||||
@ -246,6 +275,78 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static int hdc100x_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
/* Buffer is enabled. First set ACQ Mode, then attach poll func */
|
||||
mutex_lock(&data->lock);
|
||||
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE,
|
||||
HDC100X_REG_CONFIG_ACQ_MODE);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
}
|
||||
|
||||
static int hdc100x_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
/* First detach poll func, then reset ACQ mode. OK to disable buffer */
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = {
|
||||
.postenable = hdc100x_buffer_postenable,
|
||||
.predisable = hdc100x_buffer_predisable,
|
||||
};
|
||||
|
||||
static irqreturn_t hdc100x_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int delay = data->adc_int_us[0] + data->adc_int_us[1];
|
||||
int ret;
|
||||
s16 buf[8]; /* 2x s16 + padding + 8 byte timestamp */
|
||||
|
||||
/* dual read starts at temp register */
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot start measurement\n");
|
||||
goto err;
|
||||
}
|
||||
usleep_range(delay, delay + 1000);
|
||||
|
||||
ret = i2c_master_recv(client, (u8 *)buf, 4);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot read sensor data\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
err:
|
||||
mutex_unlock(&data->lock);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info hdc100x_info = {
|
||||
.read_raw = hdc100x_read_raw,
|
||||
.write_raw = hdc100x_write_raw,
|
||||
@ -258,6 +359,7 @@ static int hdc100x_probe(struct i2c_client *client,
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct hdc100x_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
|
||||
@ -279,12 +381,35 @@ static int hdc100x_probe(struct i2c_client *client,
|
||||
|
||||
indio_dev->channels = hdc100x_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
|
||||
indio_dev->available_scan_masks = hdc100x_scan_masks;
|
||||
|
||||
/* be sure we are in a known state */
|
||||
hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
|
||||
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
|
||||
hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
hdc100x_trigger_handler,
|
||||
&hdc_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdc100x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id hdc100x_id[] = {
|
||||
@ -298,6 +423,7 @@ static struct i2c_driver hdc100x_driver = {
|
||||
.name = "hdc100x",
|
||||
},
|
||||
.probe = hdc100x_probe,
|
||||
.remove = hdc100x_remove,
|
||||
.id_table = hdc100x_id,
|
||||
};
|
||||
module_i2c_driver(hdc100x_driver);
|
||||
|
73
drivers/iio/humidity/hts221.h
Normal file
73
drivers/iio/humidity/hts221.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* STMicroelectronics hts221 sensor driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef HTS221_H
|
||||
#define HTS221_H
|
||||
|
||||
#define HTS221_DEV_NAME "hts221"
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define HTS221_RX_MAX_LENGTH 8
|
||||
#define HTS221_TX_MAX_LENGTH 8
|
||||
|
||||
#define HTS221_DATA_SIZE 2
|
||||
|
||||
struct hts221_transfer_buffer {
|
||||
u8 rx_buf[HTS221_RX_MAX_LENGTH];
|
||||
u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct hts221_transfer_function {
|
||||
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
};
|
||||
|
||||
#define HTS221_AVG_DEPTH 8
|
||||
struct hts221_avg_avl {
|
||||
u16 avg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
enum hts221_sensor_type {
|
||||
HTS221_SENSOR_H,
|
||||
HTS221_SENSOR_T,
|
||||
HTS221_SENSOR_MAX,
|
||||
};
|
||||
|
||||
struct hts221_sensor {
|
||||
u8 cur_avg_idx;
|
||||
int slope, b_gen;
|
||||
};
|
||||
|
||||
struct hts221_hw {
|
||||
const char *name;
|
||||
struct device *dev;
|
||||
|
||||
struct mutex lock;
|
||||
struct iio_trigger *trig;
|
||||
int irq;
|
||||
|
||||
struct hts221_sensor sensors[HTS221_SENSOR_MAX];
|
||||
|
||||
u8 odr;
|
||||
|
||||
const struct hts221_transfer_function *tf;
|
||||
struct hts221_transfer_buffer tb;
|
||||
};
|
||||
|
||||
int hts221_config_drdy(struct hts221_hw *hw, bool enable);
|
||||
int hts221_probe(struct iio_dev *iio_dev);
|
||||
int hts221_power_on(struct hts221_hw *hw);
|
||||
int hts221_power_off(struct hts221_hw *hw);
|
||||
int hts221_allocate_buffers(struct hts221_hw *hw);
|
||||
int hts221_allocate_trigger(struct hts221_hw *hw);
|
||||
|
||||
#endif /* HTS221_H */
|
169
drivers/iio/humidity/hts221_buffer.c
Normal file
169
drivers/iio/humidity/hts221_buffer.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* STMicroelectronics hts221 sensor driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "hts221.h"
|
||||
|
||||
#define HTS221_REG_STATUS_ADDR 0x27
|
||||
#define HTS221_RH_DRDY_MASK BIT(1)
|
||||
#define HTS221_TEMP_DRDY_MASK BIT(0)
|
||||
|
||||
static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
return hts221_config_drdy(hw, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops hts221_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = hts221_trig_set_state,
|
||||
};
|
||||
|
||||
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct hts221_hw *hw = (struct hts221_hw *)private;
|
||||
u8 status;
|
||||
int err;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
|
||||
&status);
|
||||
if (err < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
* H_DA bit (humidity data available) is routed to DRDY line.
|
||||
* Humidity sample is computed after temperature one.
|
||||
* Here we can assume data channels are both available if H_DA bit
|
||||
* is set in status register
|
||||
*/
|
||||
if (!(status & HTS221_RH_DRDY_MASK))
|
||||
return IRQ_NONE;
|
||||
|
||||
iio_trigger_poll_chained(hw->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int hts221_allocate_trigger(struct hts221_hw *hw)
|
||||
{
|
||||
struct iio_dev *iio_dev = iio_priv_to_dev(hw);
|
||||
unsigned long irq_type;
|
||||
int err;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
|
||||
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
case IRQF_TRIGGER_RISING:
|
||||
break;
|
||||
default:
|
||||
dev_info(hw->dev,
|
||||
"mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
|
||||
irq_type);
|
||||
irq_type = IRQF_TRIGGER_RISING;
|
||||
break;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
|
||||
hts221_trigger_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
hw->name, hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
|
||||
iio_dev->name);
|
||||
if (!hw->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_trigger_set_drvdata(hw->trig, iio_dev);
|
||||
hw->trig->ops = &hts221_trigger_ops;
|
||||
hw->trig->dev.parent = hw->dev;
|
||||
iio_dev->trig = iio_trigger_get(hw->trig);
|
||||
|
||||
return devm_iio_trigger_register(hw->dev, hw->trig);
|
||||
}
|
||||
|
||||
static int hts221_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return hts221_power_on(iio_priv(iio_dev));
|
||||
}
|
||||
|
||||
static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return hts221_power_off(iio_priv(iio_dev));
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops hts221_buffer_ops = {
|
||||
.preenable = hts221_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = hts221_buffer_postdisable,
|
||||
};
|
||||
|
||||
static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
|
||||
{
|
||||
u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio_dev = pf->indio_dev;
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
struct iio_chan_spec const *ch;
|
||||
int err;
|
||||
|
||||
/* humidity data */
|
||||
ch = &iio_dev->channels[HTS221_SENSOR_H];
|
||||
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
|
||||
buffer);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
/* temperature data */
|
||||
ch = &iio_dev->channels[HTS221_SENSOR_T];
|
||||
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
|
||||
buffer + HTS221_DATA_SIZE);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
|
||||
iio_get_time_ns(iio_dev));
|
||||
|
||||
out:
|
||||
iio_trigger_notify_done(hw->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int hts221_allocate_buffers(struct hts221_hw *hw)
|
||||
{
|
||||
return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
|
||||
NULL, hts221_buffer_handler_thread,
|
||||
&hts221_buffer_ops);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
687
drivers/iio/humidity/hts221_core.c
Normal file
687
drivers/iio/humidity/hts221_core.c
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
* STMicroelectronics hts221 sensor driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "hts221.h"
|
||||
|
||||
#define HTS221_REG_WHOAMI_ADDR 0x0f
|
||||
#define HTS221_REG_WHOAMI_VAL 0xbc
|
||||
|
||||
#define HTS221_REG_CNTRL1_ADDR 0x20
|
||||
#define HTS221_REG_CNTRL2_ADDR 0x21
|
||||
#define HTS221_REG_CNTRL3_ADDR 0x22
|
||||
|
||||
#define HTS221_REG_AVG_ADDR 0x10
|
||||
#define HTS221_REG_H_OUT_L 0x28
|
||||
#define HTS221_REG_T_OUT_L 0x2a
|
||||
|
||||
#define HTS221_HUMIDITY_AVG_MASK 0x07
|
||||
#define HTS221_TEMP_AVG_MASK 0x38
|
||||
|
||||
#define HTS221_ODR_MASK 0x87
|
||||
#define HTS221_BDU_MASK BIT(2)
|
||||
|
||||
#define HTS221_DRDY_MASK BIT(2)
|
||||
|
||||
#define HTS221_ENABLE_SENSOR BIT(7)
|
||||
|
||||
#define HTS221_HUMIDITY_AVG_4 0x00 /* 0.4 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_8 0x01 /* 0.3 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_16 0x02 /* 0.2 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_32 0x03 /* 0.15 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_64 0x04 /* 0.1 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_128 0x05 /* 0.07 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_256 0x06 /* 0.05 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_512 0x07 /* 0.03 %RH */
|
||||
|
||||
#define HTS221_TEMP_AVG_2 0x00 /* 0.08 degC */
|
||||
#define HTS221_TEMP_AVG_4 0x08 /* 0.05 degC */
|
||||
#define HTS221_TEMP_AVG_8 0x10 /* 0.04 degC */
|
||||
#define HTS221_TEMP_AVG_16 0x18 /* 0.03 degC */
|
||||
#define HTS221_TEMP_AVG_32 0x20 /* 0.02 degC */
|
||||
#define HTS221_TEMP_AVG_64 0x28 /* 0.015 degC */
|
||||
#define HTS221_TEMP_AVG_128 0x30 /* 0.01 degC */
|
||||
#define HTS221_TEMP_AVG_256 0x38 /* 0.007 degC */
|
||||
|
||||
/* calibration registers */
|
||||
#define HTS221_REG_0RH_CAL_X_H 0x36
|
||||
#define HTS221_REG_1RH_CAL_X_H 0x3a
|
||||
#define HTS221_REG_0RH_CAL_Y_H 0x30
|
||||
#define HTS221_REG_1RH_CAL_Y_H 0x31
|
||||
#define HTS221_REG_0T_CAL_X_L 0x3c
|
||||
#define HTS221_REG_1T_CAL_X_L 0x3e
|
||||
#define HTS221_REG_0T_CAL_Y_H 0x32
|
||||
#define HTS221_REG_1T_CAL_Y_H 0x33
|
||||
#define HTS221_REG_T1_T0_CAL_Y_H 0x35
|
||||
|
||||
struct hts221_odr {
|
||||
u8 hz;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
struct hts221_avg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
|
||||
};
|
||||
|
||||
static const struct hts221_odr hts221_odr_table[] = {
|
||||
{ 1, 0x01 }, /* 1Hz */
|
||||
{ 7, 0x02 }, /* 7Hz */
|
||||
{ 13, 0x03 }, /* 12.5Hz */
|
||||
};
|
||||
|
||||
static const struct hts221_avg hts221_avg_list[] = {
|
||||
{
|
||||
.addr = HTS221_REG_AVG_ADDR,
|
||||
.mask = HTS221_HUMIDITY_AVG_MASK,
|
||||
.avg_avl = {
|
||||
{ 4, HTS221_HUMIDITY_AVG_4 },
|
||||
{ 8, HTS221_HUMIDITY_AVG_8 },
|
||||
{ 16, HTS221_HUMIDITY_AVG_16 },
|
||||
{ 32, HTS221_HUMIDITY_AVG_32 },
|
||||
{ 64, HTS221_HUMIDITY_AVG_64 },
|
||||
{ 128, HTS221_HUMIDITY_AVG_128 },
|
||||
{ 256, HTS221_HUMIDITY_AVG_256 },
|
||||
{ 512, HTS221_HUMIDITY_AVG_512 },
|
||||
},
|
||||
},
|
||||
{
|
||||
.addr = HTS221_REG_AVG_ADDR,
|
||||
.mask = HTS221_TEMP_AVG_MASK,
|
||||
.avg_avl = {
|
||||
{ 2, HTS221_TEMP_AVG_2 },
|
||||
{ 4, HTS221_TEMP_AVG_4 },
|
||||
{ 8, HTS221_TEMP_AVG_8 },
|
||||
{ 16, HTS221_TEMP_AVG_16 },
|
||||
{ 32, HTS221_TEMP_AVG_32 },
|
||||
{ 64, HTS221_TEMP_AVG_64 },
|
||||
{ 128, HTS221_TEMP_AVG_128 },
|
||||
{ 256, HTS221_TEMP_AVG_256 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec hts221_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.address = HTS221_REG_H_OUT_L,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = HTS221_REG_T_OUT_L,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read %02x register\n", addr);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
data = (data & ~mask) | (val & mask);
|
||||
|
||||
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0)
|
||||
dev_err(hw->dev, "failed to write %02x register\n", addr);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hts221_check_whoami(struct hts221_hw *hw)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
|
||||
&data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read whoami register\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data != HTS221_REG_WHOAMI_VAL) {
|
||||
dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
|
||||
data, HTS221_REG_WHOAMI_VAL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hts221_config_drdy(struct hts221_hw *hw, bool enable)
|
||||
{
|
||||
u8 val = enable ? BIT(2) : 0;
|
||||
int err;
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
|
||||
HTS221_DRDY_MASK, val);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
|
||||
{
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
|
||||
if (hts221_odr_table[i].hz == odr)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(hts221_odr_table))
|
||||
return -EINVAL;
|
||||
|
||||
val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ODR_MASK, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->odr = odr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hts221_update_avg(struct hts221_hw *hw,
|
||||
enum hts221_sensor_type type,
|
||||
u16 val)
|
||||
{
|
||||
int i, err;
|
||||
const struct hts221_avg *avg = &hts221_avg_list[type];
|
||||
|
||||
for (i = 0; i < HTS221_AVG_DEPTH; i++)
|
||||
if (avg->avg_avl[i].avg == val)
|
||||
break;
|
||||
|
||||
if (i == HTS221_AVG_DEPTH)
|
||||
return -EINVAL;
|
||||
|
||||
err = hts221_write_with_mask(hw, avg->addr, avg->mask,
|
||||
avg->avg_avl[i].val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->sensors[type].cur_avg_idx = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int i;
|
||||
ssize_t len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
hts221_odr_table[i].hz);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
hts221_sysfs_rh_oversampling_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
avg->avg_avl[i].avg);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
hts221_sysfs_temp_oversampling_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
avg->avg_avl[i].avg);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int hts221_power_on(struct hts221_hw *hw)
|
||||
{
|
||||
return hts221_update_odr(hw, hw->odr);
|
||||
}
|
||||
|
||||
int hts221_power_off(struct hts221_hw *hw)
|
||||
{
|
||||
u8 data[] = {0x00, 0x00};
|
||||
|
||||
return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
|
||||
data);
|
||||
}
|
||||
|
||||
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
|
||||
{
|
||||
int err, *slope, *b_gen;
|
||||
s16 cal_x0, cal_x1, cal_y0, cal_y1;
|
||||
u8 cal0, cal1;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
|
||||
sizeof(cal0), &cal0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
|
||||
sizeof(cal1), &cal1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
|
||||
sizeof(cal0), &cal0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
|
||||
(u8 *)&cal_x0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x0 = le16_to_cpu(cal_x0);
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
|
||||
(u8 *)&cal_x1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x1 = le16_to_cpu(cal_x1);
|
||||
|
||||
slope = &hw->sensors[HTS221_SENSOR_T].slope;
|
||||
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
|
||||
|
||||
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
|
||||
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
|
||||
(cal_x1 - cal_x0);
|
||||
*b_gen *= 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
|
||||
{
|
||||
int err, *slope, *b_gen;
|
||||
s16 cal_x0, cal_x1, cal_y0, cal_y1;
|
||||
u8 data;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
|
||||
&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y0 = data;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
|
||||
&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y1 = data;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
|
||||
(u8 *)&cal_x0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x0 = le16_to_cpu(cal_x0);
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
|
||||
(u8 *)&cal_x1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x1 = le16_to_cpu(cal_x1);
|
||||
|
||||
slope = &hw->sensors[HTS221_SENSOR_H].slope;
|
||||
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
|
||||
|
||||
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
|
||||
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
|
||||
(cal_x1 - cal_x0);
|
||||
*b_gen *= 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hts221_get_sensor_scale(struct hts221_hw *hw,
|
||||
enum iio_chan_type ch_type,
|
||||
int *val, int *val2)
|
||||
{
|
||||
s64 tmp;
|
||||
s32 rem, div, data;
|
||||
|
||||
switch (ch_type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
data = hw->sensors[HTS221_SENSOR_H].slope;
|
||||
div = (1 << 4) * 1000;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
data = hw->sensors[HTS221_SENSOR_T].slope;
|
||||
div = (1 << 6) * 1000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tmp = div_s64(data * 1000000000LL, div);
|
||||
tmp = div_s64_rem(tmp, 1000000000LL, &rem);
|
||||
|
||||
*val = tmp;
|
||||
*val2 = rem;
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static int hts221_get_sensor_offset(struct hts221_hw *hw,
|
||||
enum iio_chan_type ch_type,
|
||||
int *val, int *val2)
|
||||
{
|
||||
s64 tmp;
|
||||
s32 rem, div, data;
|
||||
|
||||
switch (ch_type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
data = hw->sensors[HTS221_SENSOR_H].b_gen;
|
||||
div = hw->sensors[HTS221_SENSOR_H].slope;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
data = hw->sensors[HTS221_SENSOR_T].b_gen;
|
||||
div = hw->sensors[HTS221_SENSOR_T].slope;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tmp = div_s64(data * 1000000000LL, div);
|
||||
tmp = div_s64_rem(tmp, 1000000000LL, &rem);
|
||||
|
||||
*val = tmp;
|
||||
*val2 = rem;
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
|
||||
{
|
||||
u8 data[HTS221_DATA_SIZE];
|
||||
int err;
|
||||
|
||||
err = hts221_power_on(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(50);
|
||||
|
||||
err = hw->tf->read(hw->dev, addr, sizeof(data), data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hts221_power_off(hw);
|
||||
|
||||
*val = (s16)get_unaligned_le16(data);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int hts221_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = hts221_read_oneshot(hw, ch->address, val);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = hw->odr;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
|
||||
u8 idx;
|
||||
const struct hts221_avg *avg;
|
||||
|
||||
switch (ch->type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
avg = &hts221_avg_list[HTS221_SENSOR_H];
|
||||
idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
|
||||
*val = avg->avg_avl[idx].avg;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
avg = &hts221_avg_list[HTS221_SENSOR_T];
|
||||
idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
|
||||
*val = avg->avg_avl[idx].avg;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hts221_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hts221_update_odr(hw, val);
|
||||
break;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hts221_validate_trigger(struct iio_dev *iio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
return hw->trig == trig ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
|
||||
hts221_sysfs_rh_oversampling_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
|
||||
hts221_sysfs_temp_oversampling_avail, NULL, 0);
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
|
||||
|
||||
static struct attribute *hts221_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group hts221_attribute_group = {
|
||||
.attrs = hts221_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info hts221_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &hts221_attribute_group,
|
||||
.read_raw = hts221_read_raw,
|
||||
.write_raw = hts221_write_raw,
|
||||
.validate_trigger = hts221_validate_trigger,
|
||||
};
|
||||
|
||||
static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
|
||||
|
||||
int hts221_probe(struct iio_dev *iio_dev)
|
||||
{
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
int err;
|
||||
u8 data;
|
||||
|
||||
mutex_init(&hw->lock);
|
||||
|
||||
err = hts221_check_whoami(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->odr = hts221_odr_table[0].hz;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
iio_dev->available_scan_masks = hts221_scan_masks;
|
||||
iio_dev->channels = hts221_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
|
||||
iio_dev->name = HTS221_DEV_NAME;
|
||||
iio_dev->info = &hts221_info;
|
||||
|
||||
/* configure humidity sensor */
|
||||
err = hts221_parse_rh_caldata(hw);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to get rh calibration data\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
|
||||
err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to set rh oversampling ratio\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* configure temperature sensor */
|
||||
err = hts221_parse_temp_caldata(hw);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev,
|
||||
"failed to get temperature calibration data\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
|
||||
err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev,
|
||||
"failed to set temperature oversampling ratio\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (hw->irq > 0) {
|
||||
err = hts221_allocate_buffers(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = hts221_allocate_trigger(hw);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(hw->dev, iio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(hts221_probe);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
110
drivers/iio/humidity/hts221_i2c.c
Normal file
110
drivers/iio/humidity/hts221_i2c.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* STMicroelectronics hts221 i2c driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include "hts221.h"
|
||||
|
||||
#define I2C_AUTO_INCREMENT 0x80
|
||||
|
||||
static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (len > 1)
|
||||
addr |= I2C_AUTO_INCREMENT;
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = client->flags;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = &addr;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = client->flags | I2C_M_RD;
|
||||
msg[1].len = len;
|
||||
msg[1].buf = data;
|
||||
|
||||
return i2c_transfer(client->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
u8 send[len + 1];
|
||||
struct i2c_msg msg;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (len > 1)
|
||||
addr |= I2C_AUTO_INCREMENT;
|
||||
|
||||
send[0] = addr;
|
||||
memcpy(&send[1], data, len * sizeof(u8));
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = client->flags;
|
||||
msg.len = len + 1;
|
||||
msg.buf = send;
|
||||
|
||||
return i2c_transfer(client->adapter, &msg, 1);
|
||||
}
|
||||
|
||||
static const struct hts221_transfer_function hts221_transfer_fn = {
|
||||
.read = hts221_i2c_read,
|
||||
.write = hts221_i2c_write,
|
||||
};
|
||||
|
||||
static int hts221_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct hts221_hw *hw;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, iio_dev);
|
||||
|
||||
hw = iio_priv(iio_dev);
|
||||
hw->name = client->name;
|
||||
hw->dev = &client->dev;
|
||||
hw->irq = client->irq;
|
||||
hw->tf = &hts221_transfer_fn;
|
||||
|
||||
return hts221_probe(iio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id hts221_i2c_of_match[] = {
|
||||
{ .compatible = "st,hts221", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id hts221_i2c_id_table[] = {
|
||||
{ HTS221_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
|
||||
|
||||
static struct i2c_driver hts221_driver = {
|
||||
.driver = {
|
||||
.name = "hts221_i2c",
|
||||
.of_match_table = of_match_ptr(hts221_i2c_of_match),
|
||||
},
|
||||
.probe = hts221_i2c_probe,
|
||||
.id_table = hts221_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(hts221_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
125
drivers/iio/humidity/hts221_spi.c
Normal file
125
drivers/iio/humidity/hts221_spi.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* STMicroelectronics hts221 spi driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include "hts221.h"
|
||||
|
||||
#define SENSORS_SPI_READ 0x80
|
||||
#define SPI_AUTO_INCREMENT 0x40
|
||||
|
||||
static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
int err;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct iio_dev *iio_dev = spi_get_drvdata(spi);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = hw->tb.rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
if (len > 1)
|
||||
addr |= SPI_AUTO_INCREMENT;
|
||||
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct iio_dev *iio_dev = spi_get_drvdata(spi);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len + 1,
|
||||
};
|
||||
|
||||
if (len >= HTS221_TX_MAX_LENGTH)
|
||||
return -ENOMEM;
|
||||
|
||||
if (len > 1)
|
||||
addr |= SPI_AUTO_INCREMENT;
|
||||
hw->tb.tx_buf[0] = addr;
|
||||
memcpy(&hw->tb.tx_buf[1], data, len);
|
||||
|
||||
return spi_sync_transfer(spi, &xfers, 1);
|
||||
}
|
||||
|
||||
static const struct hts221_transfer_function hts221_transfer_fn = {
|
||||
.read = hts221_spi_read,
|
||||
.write = hts221_spi_write,
|
||||
};
|
||||
|
||||
static int hts221_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct hts221_hw *hw;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, iio_dev);
|
||||
|
||||
hw = iio_priv(iio_dev);
|
||||
hw->name = spi->modalias;
|
||||
hw->dev = &spi->dev;
|
||||
hw->irq = spi->irq;
|
||||
hw->tf = &hts221_transfer_fn;
|
||||
|
||||
return hts221_probe(iio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id hts221_spi_of_match[] = {
|
||||
{ .compatible = "st,hts221", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
|
||||
|
||||
static const struct spi_device_id hts221_spi_id_table[] = {
|
||||
{ HTS221_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
|
||||
|
||||
static struct spi_driver hts221_driver = {
|
||||
.driver = {
|
||||
.name = "hts221_spi",
|
||||
.of_match_table = of_match_ptr(hts221_spi_of_match),
|
||||
},
|
||||
.probe = hts221_spi_probe,
|
||||
.id_table = hts221_spi_id_table,
|
||||
};
|
||||
module_spi_driver(hts221_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -398,7 +398,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
|
||||
__le16 buf[16];
|
||||
/* 3 sens x 3 axis x __le16 + 3 x __le16 pad + 4 x __le16 tstamp */
|
||||
int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
|
||||
__le16 sample;
|
||||
|
||||
|
@ -307,10 +307,9 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
|
||||
const unsigned long *mask;
|
||||
unsigned long *trialmask;
|
||||
|
||||
trialmask = kmalloc(sizeof(*trialmask)*
|
||||
BITS_TO_LONGS(indio_dev->masklength),
|
||||
GFP_KERNEL);
|
||||
|
||||
trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
|
||||
sizeof(*trialmask),
|
||||
GFP_KERNEL);
|
||||
if (trialmask == NULL)
|
||||
return -ENOMEM;
|
||||
if (!indio_dev->masklength) {
|
||||
|
@ -81,6 +81,8 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_PH] = "ph",
|
||||
[IIO_UVINDEX] = "uvindex",
|
||||
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
|
||||
[IIO_COUNT] = "count",
|
||||
[IIO_INDEX] = "index",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
@ -717,6 +717,27 @@ bool iio_trigger_using_own(struct iio_dev *indio_dev)
|
||||
}
|
||||
EXPORT_SYMBOL(iio_trigger_using_own);
|
||||
|
||||
/**
|
||||
* iio_trigger_validate_own_device - Check if a trigger and IIO device belong to
|
||||
* the same device
|
||||
* @trig: The IIO trigger to check
|
||||
* @indio_dev: the IIO device to check
|
||||
*
|
||||
* This function can be used as the validate_device callback for triggers that
|
||||
* can only be attached to their own device.
|
||||
*
|
||||
* Return: 0 if both the trigger and the IIO device belong to the same
|
||||
* device, -EINVAL otherwise.
|
||||
*/
|
||||
int iio_trigger_validate_own_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
if (indio_dev->dev.parent != trig->dev.parent)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_trigger_validate_own_device);
|
||||
|
||||
void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
|
||||
{
|
||||
indio_dev->groups[indio_dev->groupcounter++] =
|
||||
|
@ -658,6 +658,31 @@ err_unlock:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
|
||||
|
||||
static int iio_read_channel_attribute(struct iio_channel *chan,
|
||||
int *val, int *val2,
|
||||
enum iio_chan_info_enum attribute)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chan->indio_dev->info_exist_lock);
|
||||
if (chan->indio_dev->info == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
ret = iio_channel_read(chan, val, val2, attribute);
|
||||
err_unlock:
|
||||
mutex_unlock(&chan->indio_dev->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
|
||||
{
|
||||
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_channel_offset);
|
||||
|
||||
int iio_read_channel_processed(struct iio_channel *chan, int *val)
|
||||
{
|
||||
int ret;
|
||||
@ -687,19 +712,7 @@ EXPORT_SYMBOL_GPL(iio_read_channel_processed);
|
||||
|
||||
int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chan->indio_dev->info_exist_lock);
|
||||
if (chan->indio_dev->info == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
|
||||
err_unlock:
|
||||
mutex_unlock(&chan->indio_dev->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_channel_scale);
|
||||
|
||||
|
@ -140,6 +140,18 @@ config GP2AP020A00F
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gp2ap020a00f.
|
||||
|
||||
config SENSORS_ISL29018
|
||||
tristate "Intersil 29018 light and proximity sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for ambient light sensing and
|
||||
proximity infrared sensing from Intersil ISL29018.
|
||||
This driver will provide the measurements of ambient light intensity
|
||||
in lux, proximity infrared sensing and normal infrared sensing.
|
||||
Data from sensor is accessible via sysfs.
|
||||
|
||||
config ISL29125
|
||||
tristate "Intersil ISL29125 digital color light sensor"
|
||||
depends on I2C
|
||||
|
@ -17,6 +17,7 @@ obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
|
||||
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
|
||||
obj-$(CONFIG_ISL29125) += isl29125.o
|
||||
obj-$(CONFIG_JSA1212) += jsa1212.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
|
@ -62,16 +62,6 @@
|
||||
#define ISL29035_BOUT_SHIFT 0x07
|
||||
#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
|
||||
|
||||
#define ISL29018_INT_TIME_AVAIL "0.090000 0.005630 0.000351 0.000021"
|
||||
#define ISL29023_INT_TIME_AVAIL "0.090000 0.005600 0.000352 0.000022"
|
||||
#define ISL29035_INT_TIME_AVAIL "0.105000 0.006500 0.000410 0.000025"
|
||||
|
||||
static const char * const int_time_avail[] = {
|
||||
ISL29018_INT_TIME_AVAIL,
|
||||
ISL29023_INT_TIME_AVAIL,
|
||||
ISL29035_INT_TIME_AVAIL,
|
||||
};
|
||||
|
||||
enum isl29018_int_time {
|
||||
ISL29018_INT_TIME_16,
|
||||
ISL29018_INT_TIME_12,
|
||||
@ -110,7 +100,8 @@ struct isl29018_chip {
|
||||
static int isl29018_set_integration_time(struct isl29018_chip *chip,
|
||||
unsigned int utime)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
unsigned int int_time, new_int_time;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
|
||||
@ -145,7 +136,8 @@ static int isl29018_set_integration_time(struct isl29018_chip *chip,
|
||||
|
||||
static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
struct isl29018_scale new_scale;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
|
||||
@ -276,29 +268,35 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t isl29018_show_scale_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t in_illuminance_scale_available_show
|
||||
(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||
int i, len = 0;
|
||||
unsigned int i;
|
||||
int len = 0;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
|
||||
len += sprintf(buf + len, "%d.%06d ",
|
||||
isl29018_scales[chip->int_time][i].scale,
|
||||
isl29018_scales[chip->int_time][i].uscale);
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t isl29018_show_int_time_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t in_illuminance_integration_time_available_show
|
||||
(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||
int i, len = 0;
|
||||
unsigned int i;
|
||||
int len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
|
||||
len += sprintf(buf + len, "0.%06d ",
|
||||
@ -309,9 +307,27 @@ static ssize_t isl29018_show_int_time_available(struct device *dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
/*
|
||||
* From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
|
||||
* infrared suppression:
|
||||
*
|
||||
* Proximity Sensing Scheme: Bit 7. This bit programs the function
|
||||
* of the proximity detection. Logic 0 of this bit, Scheme 0, makes
|
||||
* full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range
|
||||
* of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit,
|
||||
* Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary)
|
||||
* proximity_less_ambient detection. The range of Scheme 1
|
||||
* proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended
|
||||
* for resolutions less than 16. While Scheme 0 has wider dynamic
|
||||
* range, Scheme 1 proximity detection is less affected by the
|
||||
* ambient IR noise variation.
|
||||
*
|
||||
* 0 Sensing IR from LED and ambient
|
||||
* 1 Sensing IR from LED with ambient IR rejection
|
||||
*/
|
||||
static ssize_t proximity_on_chip_ambient_infrared_suppression_show
|
||||
(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||
@ -323,9 +339,9 @@ static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
|
||||
return sprintf(buf, "%d\n", chip->prox_scheme);
|
||||
}
|
||||
|
||||
static ssize_t isl29018_store_prox_infrared_suppression(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t proximity_on_chip_ambient_infrared_suppression_store
|
||||
(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||
@ -357,6 +373,10 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended) {
|
||||
ret = -EBUSY;
|
||||
goto write_done;
|
||||
}
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
@ -366,13 +386,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
if (val) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (chan->type == IIO_LIGHT && !val)
|
||||
ret = isl29018_set_integration_time(chip, val2);
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
@ -381,6 +396,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
write_done:
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return ret;
|
||||
@ -397,8 +414,8 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto read_done;
|
||||
}
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@ -445,7 +462,10 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
read_done:
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -482,14 +502,9 @@ static const struct iio_chan_spec isl29023_channels[] = {
|
||||
ISL29018_IR_CHANNEL,
|
||||
};
|
||||
|
||||
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
|
||||
isl29018_show_int_time_available, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
|
||||
isl29018_show_scale_available, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
|
||||
S_IRUGO | S_IWUSR,
|
||||
isl29018_show_prox_infrared_suppression,
|
||||
isl29018_store_prox_infrared_suppression, 0);
|
||||
static IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0);
|
||||
static IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0);
|
||||
static IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0);
|
||||
|
||||
#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
||||
|
||||
@ -514,30 +529,6 @@ static const struct attribute_group isl29023_group = {
|
||||
.attrs = isl29023_attributes,
|
||||
};
|
||||
|
||||
static int isl29035_detect(struct isl29018_chip *chip)
|
||||
{
|
||||
int status;
|
||||
unsigned int id;
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
|
||||
status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
|
||||
if (status < 0) {
|
||||
dev_err(dev,
|
||||
"Error reading ID register with error %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
|
||||
|
||||
if (id != ISL29035_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
/* Clear brownout bit */
|
||||
return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
|
||||
ISL29035_BOUT_MASK, 0);
|
||||
}
|
||||
|
||||
enum {
|
||||
isl29018,
|
||||
isl29023,
|
||||
@ -550,12 +541,31 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
|
||||
if (chip->type == isl29035) {
|
||||
status = isl29035_detect(chip);
|
||||
unsigned int id;
|
||||
|
||||
status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
|
||||
if (status < 0) {
|
||||
dev_err(dev,
|
||||
"Error reading ID register with error %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
|
||||
|
||||
if (id != ISL29035_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
/* Clear brownout bit */
|
||||
status = regmap_update_bits(chip->regmap,
|
||||
ISL29035_REG_DEVICE_ID,
|
||||
ISL29035_BOUT_MASK, 0);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Code added per Intersil Application Note 1534:
|
||||
/*
|
||||
* Code added per Intersil Application Note 1534:
|
||||
* When VDD sinks to approximately 1.8V or below, some of
|
||||
* the part's registers may change their state. When VDD
|
||||
* recovers to 2.25V (or greater), the part may thus be in an
|
||||
@ -582,7 +592,8 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* See Intersil AN1534 comments above.
|
||||
/*
|
||||
* See Intersil AN1534 comments above.
|
||||
* "Operating Mode" (COMMAND1) register is reprogrammed when
|
||||
* data is read from the device.
|
||||
*/
|
||||
@ -605,12 +616,10 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
|
||||
|
||||
status = isl29018_set_integration_time(chip,
|
||||
isl29018_int_utimes[chip->type][chip->int_time]);
|
||||
if (status < 0) {
|
||||
if (status < 0)
|
||||
dev_err(dev, "Init of isl29018 fails\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct iio_info isl29018_info = {
|
||||
@ -713,6 +722,7 @@ static int isl29018_probe(struct i2c_client *client,
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
chip = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
@ -752,6 +762,7 @@ static int isl29018_probe(struct i2c_client *client,
|
||||
indio_dev->name = name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
@ -762,13 +773,15 @@ static int isl29018_suspend(struct device *dev)
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
/* Since this driver uses only polling commands, we are by default in
|
||||
/*
|
||||
* Since this driver uses only polling commands, we are by default in
|
||||
* auto shutdown (ie, power-down) mode.
|
||||
* So we do not have much to do here.
|
||||
*/
|
||||
chip->suspended = true;
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -784,6 +797,7 @@ static int isl29018_resume(struct device *dev)
|
||||
chip->suspended = false;
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -807,7 +821,6 @@ static const struct i2c_device_id isl29018_id[] = {
|
||||
{"isl29035", isl29035},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, isl29018_id);
|
||||
|
||||
static const struct of_device_id isl29018_of_match[] = {
|
@ -631,14 +631,16 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->lock_als);
|
||||
ret = ltr501_read_als(data, buf);
|
||||
mutex_unlock(&data->lock_als);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
|
||||
@ -648,8 +650,9 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
@ -657,21 +660,28 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
|
||||
ret = ltr501_read_als(data, buf);
|
||||
mutex_unlock(&data->lock_als);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
|
||||
buf[0] : buf[1]);
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
mutex_lock(&data->lock_ps);
|
||||
ret = ltr501_read_ps(data);
|
||||
mutex_unlock(&data->lock_ps);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
*val = ret & LTR501_PS_DATA_MASK;
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
@ -729,8 +739,9 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
|
||||
int i, ret, freq_val, freq_val2;
|
||||
struct ltr501_chip_info *info = data->chip_info;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
@ -739,85 +750,105 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
|
||||
i = ltr501_get_gain_index(info->als_gain,
|
||||
info->als_gain_tbl_size,
|
||||
val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
if (i < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
data->als_contr &= ~info->als_gain_mask;
|
||||
data->als_contr |= i << info->als_gain_shift;
|
||||
|
||||
return regmap_write(data->regmap, LTR501_ALS_CONTR,
|
||||
data->als_contr);
|
||||
ret = regmap_write(data->regmap, LTR501_ALS_CONTR,
|
||||
data->als_contr);
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
i = ltr501_get_gain_index(info->ps_gain,
|
||||
info->ps_gain_tbl_size,
|
||||
val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
if (i < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
|
||||
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
|
||||
|
||||
return regmap_write(data->regmap, LTR501_PS_CONTR,
|
||||
data->ps_contr);
|
||||
ret = regmap_write(data->regmap, LTR501_PS_CONTR,
|
||||
data->ps_contr);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
if (val != 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_lock(&data->lock_als);
|
||||
i = ltr501_set_it_time(data, val2);
|
||||
ret = ltr501_set_it_time(data, val2);
|
||||
mutex_unlock(&data->lock_als);
|
||||
return i;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
ret = ltr501_als_read_samp_freq(data, &freq_val,
|
||||
&freq_val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
ret = ltr501_als_write_samp_freq(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
/* update persistence count when changing frequency */
|
||||
ret = ltr501_write_intr_prst(data, chan->type,
|
||||
0, data->als_period);
|
||||
|
||||
if (ret < 0)
|
||||
return ltr501_als_write_samp_freq(data,
|
||||
freq_val,
|
||||
freq_val2);
|
||||
return ret;
|
||||
ret = ltr501_als_write_samp_freq(data, freq_val,
|
||||
freq_val2);
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
ret = ltr501_ps_read_samp_freq(data, &freq_val,
|
||||
&freq_val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
ret = ltr501_ps_write_samp_freq(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
/* update persistence count when changing frequency */
|
||||
ret = ltr501_write_intr_prst(data, chan->type,
|
||||
0, data->ps_period);
|
||||
|
||||
if (ret < 0)
|
||||
return ltr501_ps_write_samp_freq(data,
|
||||
freq_val,
|
||||
freq_val2);
|
||||
return ret;
|
||||
ret = ltr501_ps_write_samp_freq(data, freq_val,
|
||||
freq_val2);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltr501_read_thresh(struct iio_dev *indio_dev,
|
||||
|
@ -204,17 +204,18 @@ static int max44000_write_alspga(struct max44000_data *data, int val)
|
||||
static int max44000_read_alsval(struct max44000_data *data)
|
||||
{
|
||||
u16 regval;
|
||||
__be16 val;
|
||||
int alstim, ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
|
||||
®val, sizeof(regval));
|
||||
&val, sizeof(val));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
alstim = ret = max44000_read_alstim(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regval = be16_to_cpu(regval);
|
||||
regval = be16_to_cpu(val);
|
||||
|
||||
/*
|
||||
* Overflow is explained on datasheet page 17.
|
||||
|
@ -287,7 +287,7 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
|
||||
static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
|
||||
{
|
||||
unsigned int src;
|
||||
int ret;
|
||||
@ -395,7 +395,7 @@ static int ak8974_selftest(struct ak8974 *ak8974)
|
||||
static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
u16 bulk;
|
||||
__le16 bulk;
|
||||
|
||||
ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
|
||||
if (ret)
|
||||
@ -453,7 +453,7 @@ static int ak8974_read_raw(struct iio_dev *indio_dev,
|
||||
long mask)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
s16 hw_values[3];
|
||||
__le16 hw_values[3];
|
||||
int ret = -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(&ak8974->i2c->dev);
|
||||
@ -494,7 +494,7 @@ static void ak8974_fill_buffer(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
s16 hw_values[8]; /* Three axes + 64bit padding */
|
||||
__le16 hw_values[8]; /* Three axes + 64bit padding */
|
||||
|
||||
pm_runtime_get_sync(&ak8974->i2c->dev);
|
||||
mutex_lock(&ak8974->lock);
|
||||
|
@ -690,6 +690,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
||||
struct ak8975_data *data = iio_priv(indio_dev);
|
||||
const struct i2c_client *client = data->client;
|
||||
const struct ak_def *def = data->def;
|
||||
__le16 rval;
|
||||
u16 buff;
|
||||
int ret;
|
||||
|
||||
@ -703,7 +704,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data_or_emulated(
|
||||
client, def->data_regs[index],
|
||||
sizeof(buff), (u8*)&buff);
|
||||
sizeof(rval), (u8*)&rval);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
@ -713,7 +714,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
||||
pm_runtime_put_autosuspend(&data->client->dev);
|
||||
|
||||
/* Swap bytes and convert to valid range. */
|
||||
buff = le16_to_cpu(buff);
|
||||
buff = le16_to_cpu(rval);
|
||||
*val = clamp_t(s16, buff, -def->range, def->range);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
@ -813,6 +814,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
|
||||
const struct ak_def *def = data->def;
|
||||
int ret;
|
||||
s16 buff[8]; /* 3 x 16 bits axis values + 1 aligned 64 bits timestamp */
|
||||
__le16 fval[3];
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
@ -826,17 +828,17 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
|
||||
*/
|
||||
ret = i2c_smbus_read_i2c_block_data_or_emulated(client,
|
||||
def->data_regs[0],
|
||||
3 * sizeof(buff[0]),
|
||||
(u8 *)buff);
|
||||
3 * sizeof(fval[0]),
|
||||
(u8 *)fval);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
/* Clamp to valid range. */
|
||||
buff[0] = clamp_t(s16, le16_to_cpu(buff[0]), -def->range, def->range);
|
||||
buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range);
|
||||
buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range);
|
||||
buff[0] = clamp_t(s16, le16_to_cpu(fval[0]), -def->range, def->range);
|
||||
buff[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range);
|
||||
buff[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buff,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
22
drivers/iio/potentiostat/Kconfig
Normal file
22
drivers/iio/potentiostat/Kconfig
Normal file
@ -0,0 +1,22 @@
|
||||
#
|
||||
# Potentiostat drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Digital potentiostats"
|
||||
|
||||
config LMP91000
|
||||
tristate "Texas Instruments LMP91000 potentiostat driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_BUFFER_CB
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Texas Instruments
|
||||
LMP91000 digital potentiostat chip.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lmp91000
|
||||
|
||||
endmenu
|
6
drivers/iio/potentiostat/Makefile
Normal file
6
drivers/iio/potentiostat/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for industrial I/O potentiostat drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_LMP91000) += lmp91000.o
|
446
drivers/iio/potentiostat/lmp91000.c
Normal file
446
drivers/iio/potentiostat/lmp91000.c
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* lmp91000.c - Support for Texas Instruments digital potentiostats
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* TODO: bias voltage + polarity control, and multiple chip support
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define LMP91000_REG_LOCK 0x01
|
||||
#define LMP91000_REG_TIACN 0x10
|
||||
#define LMP91000_REG_TIACN_GAIN_SHIFT 2
|
||||
|
||||
#define LMP91000_REG_REFCN 0x11
|
||||
#define LMP91000_REG_REFCN_EXT_REF 0x20
|
||||
#define LMP91000_REG_REFCN_50_ZERO 0x80
|
||||
|
||||
#define LMP91000_REG_MODECN 0x12
|
||||
#define LMP91000_REG_MODECN_3LEAD 0x03
|
||||
#define LMP91000_REG_MODECN_TEMP 0x07
|
||||
|
||||
#define LMP91000_DRV_NAME "lmp91000"
|
||||
|
||||
static const int lmp91000_tia_gain[] = { 0, 2750, 3500, 7000, 14000, 35000,
|
||||
120000, 350000 };
|
||||
|
||||
static const int lmp91000_rload[] = { 10, 33, 50, 100 };
|
||||
|
||||
#define LMP91000_TEMP_BASE -40
|
||||
|
||||
static const u16 lmp91000_temp_lut[] = {
|
||||
1875, 1867, 1860, 1852, 1844, 1836, 1828, 1821, 1813, 1805,
|
||||
1797, 1789, 1782, 1774, 1766, 1758, 1750, 1742, 1734, 1727,
|
||||
1719, 1711, 1703, 1695, 1687, 1679, 1671, 1663, 1656, 1648,
|
||||
1640, 1632, 1624, 1616, 1608, 1600, 1592, 1584, 1576, 1568,
|
||||
1560, 1552, 1544, 1536, 1528, 1520, 1512, 1504, 1496, 1488,
|
||||
1480, 1472, 1464, 1456, 1448, 1440, 1432, 1424, 1415, 1407,
|
||||
1399, 1391, 1383, 1375, 1367, 1359, 1351, 1342, 1334, 1326,
|
||||
1318, 1310, 1302, 1293, 1285, 1277, 1269, 1261, 1253, 1244,
|
||||
1236, 1228, 1220, 1212, 1203, 1195, 1187, 1179, 1170, 1162,
|
||||
1154, 1146, 1137, 1129, 1121, 1112, 1104, 1096, 1087, 1079,
|
||||
1071, 1063, 1054, 1046, 1038, 1029, 1021, 1012, 1004, 996,
|
||||
987, 979, 971, 962, 954, 945, 937, 929, 920, 912,
|
||||
903, 895, 886, 878, 870, 861 };
|
||||
|
||||
static const struct regmap_config lmp91000_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
struct lmp91000_data {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
|
||||
struct iio_trigger *trig;
|
||||
struct iio_cb_buffer *cb_buffer;
|
||||
struct iio_channel *adc_chan;
|
||||
|
||||
struct completion completion;
|
||||
u8 chan_select;
|
||||
|
||||
u32 buffer[4]; /* 64-bit data + 64-bit timestamp */
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec lmp91000_channels[] = {
|
||||
{ /* chemical channel mV */
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.address = LMP91000_REG_MODECN_3LEAD,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
{ /* temperature channel mV */
|
||||
.type = IIO_TEMP,
|
||||
.channel = 1,
|
||||
.address = LMP91000_REG_MODECN_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.scan_index = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static int lmp91000_read(struct lmp91000_data *data, int channel, int *val)
|
||||
{
|
||||
int state, ret;
|
||||
|
||||
ret = regmap_read(data->regmap, LMP91000_REG_MODECN, &state);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_write(data->regmap, LMP91000_REG_MODECN, channel);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
/* delay till first temperature reading is complete */
|
||||
if ((state != channel) && (channel == LMP91000_REG_MODECN_TEMP))
|
||||
usleep_range(3000, 4000);
|
||||
|
||||
data->chan_select = channel != LMP91000_REG_MODECN_3LEAD;
|
||||
|
||||
iio_trigger_poll_chained(data->trig);
|
||||
|
||||
ret = wait_for_completion_timeout(&data->completion, HZ);
|
||||
reinit_completion(&data->completion);
|
||||
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
*val = data->buffer[data->chan_select];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t lmp91000_buffer_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
int ret, val;
|
||||
|
||||
memset(data->buffer, 0, sizeof(data->buffer));
|
||||
|
||||
ret = lmp91000_read(data, LMP91000_REG_MODECN_3LEAD, &val);
|
||||
if (!ret) {
|
||||
data->buffer[0] = val;
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int lmp91000_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_PROCESSED: {
|
||||
int ret = iio_channel_start_all_cb(data->cb_buffer);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lmp91000_read(data, chan->address, val);
|
||||
|
||||
iio_channel_stop_all_cb(data->cb_buffer);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_PROCESSED) {
|
||||
int tmp, i;
|
||||
|
||||
ret = iio_convert_raw_to_processed(data->adc_chan,
|
||||
*val, &tmp, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lmp91000_temp_lut); i++)
|
||||
if (lmp91000_temp_lut[i] < tmp)
|
||||
break;
|
||||
|
||||
*val = (LMP91000_TEMP_BASE + i) * 1000;
|
||||
}
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
return iio_read_channel_offset(data->adc_chan, val, val2);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return iio_read_channel_scale(data->adc_chan, val, val2);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info lmp91000_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = lmp91000_read_raw,
|
||||
};
|
||||
|
||||
static int lmp91000_read_config(struct lmp91000_data *data)
|
||||
{
|
||||
struct device *dev = data->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned int reg, val;
|
||||
int i, ret;
|
||||
|
||||
ret = of_property_read_u32(np, "ti,tia-gain-ohm", &val);
|
||||
if (ret) {
|
||||
if (of_property_read_bool(np, "ti,external-tia-resistor"))
|
||||
val = 0;
|
||||
else {
|
||||
dev_err(dev, "no ti,tia-gain-ohm defined");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(lmp91000_tia_gain); i++) {
|
||||
if (lmp91000_tia_gain[i] == val) {
|
||||
reg = i << LMP91000_REG_TIACN_GAIN_SHIFT;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid ti,tia-gain-ohm %d\n", val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "ti,rload-ohm", &val);
|
||||
if (ret) {
|
||||
val = 100;
|
||||
dev_info(dev, "no ti,rload-ohm defined, default to %d\n", val);
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(lmp91000_rload); i++) {
|
||||
if (lmp91000_rload[i] == val) {
|
||||
reg |= i;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid ti,rload-ohm %d\n", val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_write(data->regmap, LMP91000_REG_LOCK, 0);
|
||||
regmap_write(data->regmap, LMP91000_REG_TIACN, reg);
|
||||
regmap_write(data->regmap, LMP91000_REG_REFCN, LMP91000_REG_REFCN_EXT_REF
|
||||
| LMP91000_REG_REFCN_50_ZERO);
|
||||
regmap_write(data->regmap, LMP91000_REG_LOCK, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lmp91000_buffer_cb(const void *val, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->buffer[data->chan_select] = *((int *)val);
|
||||
complete_all(&data->completion);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops lmp91000_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
|
||||
return iio_channel_start_all_cb(data->cb_buffer);
|
||||
}
|
||||
|
||||
static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_channel_stop_all_cb(data->cb_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = {
|
||||
.preenable = lmp91000_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = lmp91000_buffer_predisable,
|
||||
};
|
||||
|
||||
static int lmp91000_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct lmp91000_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &lmp91000_info;
|
||||
indio_dev->channels = lmp91000_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lmp91000_channels);
|
||||
indio_dev->name = LMP91000_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->dev = dev;
|
||||
data->regmap = devm_regmap_init_i2c(client, &lmp91000_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(dev, "regmap initialization failed.\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
if (!data->trig) {
|
||||
dev_err(dev, "cannot allocate iio trigger.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->trig->ops = &lmp91000_trigger_ops;
|
||||
data->trig->dev.parent = dev;
|
||||
init_completion(&data->completion);
|
||||
|
||||
ret = lmp91000_read_config(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_trigger_set_immutable(iio_channel_cb_get_iio_dev(data->cb_buffer),
|
||||
data->trig);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot set immutable trigger.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_trigger_register(data->trig);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot register iio trigger.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&lmp91000_buffer_handler,
|
||||
&lmp91000_buffer_setup_ops);
|
||||
if (ret)
|
||||
goto error_unreg_trigger;
|
||||
|
||||
data->cb_buffer = iio_channel_get_all_cb(dev, &lmp91000_buffer_cb,
|
||||
indio_dev);
|
||||
|
||||
if (IS_ERR(data->cb_buffer)) {
|
||||
if (PTR_ERR(data->cb_buffer) == -ENODEV)
|
||||
ret = -EPROBE_DEFER;
|
||||
else
|
||||
ret = PTR_ERR(data->cb_buffer);
|
||||
|
||||
goto error_unreg_buffer;
|
||||
}
|
||||
|
||||
data->adc_chan = iio_channel_cb_get_channels(data->cb_buffer);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_cb_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_cb_buffer:
|
||||
iio_channel_release_all_cb(data->cb_buffer);
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
error_unreg_trigger:
|
||||
iio_trigger_unregister(data->trig);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lmp91000_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_channel_stop_all_cb(data->cb_buffer);
|
||||
iio_channel_release_all_cb(data->cb_buffer);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
iio_trigger_unregister(data->trig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lmp91000_of_match[] = {
|
||||
{ .compatible = "ti,lmp91000", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lmp91000_of_match);
|
||||
|
||||
static const struct i2c_device_id lmp91000_id[] = {
|
||||
{ "lmp91000", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lmp91000_id);
|
||||
|
||||
static struct i2c_driver lmp91000_driver = {
|
||||
.driver = {
|
||||
.name = LMP91000_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(lmp91000_of_match),
|
||||
},
|
||||
.probe = lmp91000_probe,
|
||||
.remove = lmp91000_remove,
|
||||
.id_table = lmp91000_id,
|
||||
};
|
||||
module_i2c_driver(lmp91000_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
|
||||
MODULE_LICENSE("GPL");
|
@ -5,6 +5,16 @@
|
||||
|
||||
menu "Pressure sensors"
|
||||
|
||||
config ABP060MG
|
||||
tristate "Honeywell ABP pressure sensor driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Honeywell ABP pressure
|
||||
sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called abp060mg.
|
||||
|
||||
config BMP280
|
||||
tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
|
||||
depends on (I2C || SPI_MASTER)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ABP060MG) += abp060mg.o
|
||||
obj-$(CONFIG_BMP280) += bmp280.o
|
||||
bmp280-objs := bmp280-core.o bmp280-regmap.o
|
||||
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
|
||||
|
276
drivers/iio/pressure/abp060mg.c
Normal file
276
drivers/iio/pressure/abp060mg.c
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (C) 2016 - Marcin Malagowski <mrc@bourne.st>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define ABP060MG_ERROR_MASK 0xC000
|
||||
#define ABP060MG_RESP_TIME_MS 40
|
||||
#define ABP060MG_MIN_COUNTS 1638 /* = 0x0666 (10% of u14) */
|
||||
#define ABP060MG_MAX_COUNTS 14745 /* = 0x3999 (90% of u14) */
|
||||
#define ABP060MG_NUM_COUNTS (ABP060MG_MAX_COUNTS - ABP060MG_MIN_COUNTS)
|
||||
|
||||
enum abp_variant {
|
||||
/* gage [kPa] */
|
||||
ABP006KG, ABP010KG, ABP016KG, ABP025KG, ABP040KG, ABP060KG, ABP100KG,
|
||||
ABP160KG, ABP250KG, ABP400KG, ABP600KG, ABP001GG,
|
||||
/* differential [kPa] */
|
||||
ABP006KD, ABP010KD, ABP016KD, ABP025KD, ABP040KD, ABP060KD, ABP100KD,
|
||||
ABP160KD, ABP250KD, ABP400KD,
|
||||
/* gage [psi] */
|
||||
ABP001PG, ABP005PG, ABP015PG, ABP030PG, ABP060PG, ABP100PG, ABP150PG,
|
||||
/* differential [psi] */
|
||||
ABP001PD, ABP005PD, ABP015PD, ABP030PD, ABP060PD,
|
||||
};
|
||||
|
||||
struct abp_config {
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static struct abp_config abp_config[] = {
|
||||
/* mbar & kPa variants */
|
||||
[ABP006KG] = { .min = 0, .max = 6000 },
|
||||
[ABP010KG] = { .min = 0, .max = 10000 },
|
||||
[ABP016KG] = { .min = 0, .max = 16000 },
|
||||
[ABP025KG] = { .min = 0, .max = 25000 },
|
||||
[ABP040KG] = { .min = 0, .max = 40000 },
|
||||
[ABP060KG] = { .min = 0, .max = 60000 },
|
||||
[ABP100KG] = { .min = 0, .max = 100000 },
|
||||
[ABP160KG] = { .min = 0, .max = 160000 },
|
||||
[ABP250KG] = { .min = 0, .max = 250000 },
|
||||
[ABP400KG] = { .min = 0, .max = 400000 },
|
||||
[ABP600KG] = { .min = 0, .max = 600000 },
|
||||
[ABP001GG] = { .min = 0, .max = 1000000 },
|
||||
[ABP006KD] = { .min = -6000, .max = 6000 },
|
||||
[ABP010KD] = { .min = -10000, .max = 10000 },
|
||||
[ABP016KD] = { .min = -16000, .max = 16000 },
|
||||
[ABP025KD] = { .min = -25000, .max = 25000 },
|
||||
[ABP040KD] = { .min = -40000, .max = 40000 },
|
||||
[ABP060KD] = { .min = -60000, .max = 60000 },
|
||||
[ABP100KD] = { .min = -100000, .max = 100000 },
|
||||
[ABP160KD] = { .min = -160000, .max = 160000 },
|
||||
[ABP250KD] = { .min = -250000, .max = 250000 },
|
||||
[ABP400KD] = { .min = -400000, .max = 400000 },
|
||||
/* psi variants (1 psi ~ 6895 Pa) */
|
||||
[ABP001PG] = { .min = 0, .max = 6985 },
|
||||
[ABP005PG] = { .min = 0, .max = 34474 },
|
||||
[ABP015PG] = { .min = 0, .max = 103421 },
|
||||
[ABP030PG] = { .min = 0, .max = 206843 },
|
||||
[ABP060PG] = { .min = 0, .max = 413686 },
|
||||
[ABP100PG] = { .min = 0, .max = 689476 },
|
||||
[ABP150PG] = { .min = 0, .max = 1034214 },
|
||||
[ABP001PD] = { .min = -6895, .max = 6895 },
|
||||
[ABP005PD] = { .min = -34474, .max = 34474 },
|
||||
[ABP015PD] = { .min = -103421, .max = 103421 },
|
||||
[ABP030PD] = { .min = -206843, .max = 206843 },
|
||||
[ABP060PD] = { .min = -413686, .max = 413686 },
|
||||
};
|
||||
|
||||
struct abp_state {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
|
||||
/*
|
||||
* bus-dependent MEASURE_REQUEST length.
|
||||
* If no SMBUS_QUICK support, need to send dummy byte
|
||||
*/
|
||||
int mreq_len;
|
||||
|
||||
/* model-dependent values (calculated on probe) */
|
||||
int scale;
|
||||
int offset;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec abp060mg_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int abp060mg_get_measurement(struct abp_state *state, int *val)
|
||||
{
|
||||
struct i2c_client *client = state->client;
|
||||
__be16 buf[2];
|
||||
u16 pressure;
|
||||
int ret;
|
||||
|
||||
buf[0] = 0;
|
||||
ret = i2c_master_send(client, (u8 *)&buf, state->mreq_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
msleep_interruptible(ABP060MG_RESP_TIME_MS);
|
||||
|
||||
ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pressure = be16_to_cpu(buf[0]);
|
||||
if (pressure & ABP060MG_ERROR_MASK)
|
||||
return -EIO;
|
||||
|
||||
if (pressure < ABP060MG_MIN_COUNTS || pressure > ABP060MG_MAX_COUNTS)
|
||||
return -EIO;
|
||||
|
||||
*val = pressure;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int abp060mg_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct abp_state *state = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = abp060mg_get_measurement(state, val);
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = state->offset;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = state->scale;
|
||||
*val2 = ABP060MG_NUM_COUNTS * 1000; /* to kPa */
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&state->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info abp060mg_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = abp060mg_read_raw,
|
||||
};
|
||||
|
||||
static void abp060mg_init_device(struct iio_dev *indio_dev, unsigned long id)
|
||||
{
|
||||
struct abp_state *state = iio_priv(indio_dev);
|
||||
struct abp_config *cfg = &abp_config[id];
|
||||
|
||||
state->scale = cfg->max - cfg->min;
|
||||
state->offset = -ABP060MG_MIN_COUNTS;
|
||||
|
||||
if (cfg->min < 0) /* differential */
|
||||
state->offset -= ABP060MG_NUM_COUNTS >> 1;
|
||||
}
|
||||
|
||||
static int abp060mg_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct abp_state *state;
|
||||
unsigned long cfg_id = id->driver_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, state);
|
||||
state->client = client;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK))
|
||||
state->mreq_len = 1;
|
||||
|
||||
abp060mg_init_device(indio_dev, cfg_id);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &abp060mg_info;
|
||||
|
||||
indio_dev->channels = abp060mg_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(abp060mg_channels);
|
||||
|
||||
mutex_init(&state->lock);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id abp060mg_id_table[] = {
|
||||
/* mbar & kPa variants (abp060m [60 mbar] == abp006k [6 kPa]) */
|
||||
/* gage: */
|
||||
{ "abp060mg", ABP006KG }, { "abp006kg", ABP006KG },
|
||||
{ "abp100mg", ABP010KG }, { "abp010kg", ABP010KG },
|
||||
{ "abp160mg", ABP016KG }, { "abp016kg", ABP016KG },
|
||||
{ "abp250mg", ABP025KG }, { "abp025kg", ABP025KG },
|
||||
{ "abp400mg", ABP040KG }, { "abp040kg", ABP040KG },
|
||||
{ "abp600mg", ABP060KG }, { "abp060kg", ABP060KG },
|
||||
{ "abp001bg", ABP100KG }, { "abp100kg", ABP100KG },
|
||||
{ "abp1_6bg", ABP160KG }, { "abp160kg", ABP160KG },
|
||||
{ "abp2_5bg", ABP250KG }, { "abp250kg", ABP250KG },
|
||||
{ "abp004bg", ABP400KG }, { "abp400kg", ABP400KG },
|
||||
{ "abp006bg", ABP600KG }, { "abp600kg", ABP600KG },
|
||||
{ "abp010bg", ABP001GG }, { "abp001gg", ABP001GG },
|
||||
/* differential: */
|
||||
{ "abp060md", ABP006KD }, { "abp006kd", ABP006KD },
|
||||
{ "abp100md", ABP010KD }, { "abp010kd", ABP010KD },
|
||||
{ "abp160md", ABP016KD }, { "abp016kd", ABP016KD },
|
||||
{ "abp250md", ABP025KD }, { "abp025kd", ABP025KD },
|
||||
{ "abp400md", ABP040KD }, { "abp040kd", ABP040KD },
|
||||
{ "abp600md", ABP060KD }, { "abp060kd", ABP060KD },
|
||||
{ "abp001bd", ABP100KD }, { "abp100kd", ABP100KD },
|
||||
{ "abp1_6bd", ABP160KD }, { "abp160kd", ABP160KD },
|
||||
{ "abp2_5bd", ABP250KD }, { "abp250kd", ABP250KD },
|
||||
{ "abp004bd", ABP400KD }, { "abp400kd", ABP400KD },
|
||||
/* psi variants */
|
||||
/* gage: */
|
||||
{ "abp001pg", ABP001PG },
|
||||
{ "abp005pg", ABP005PG },
|
||||
{ "abp015pg", ABP015PG },
|
||||
{ "abp030pg", ABP030PG },
|
||||
{ "abp060pg", ABP060PG },
|
||||
{ "abp100pg", ABP100PG },
|
||||
{ "abp150pg", ABP150PG },
|
||||
/* differential: */
|
||||
{ "abp001pd", ABP001PD },
|
||||
{ "abp005pd", ABP005PD },
|
||||
{ "abp015pd", ABP015PD },
|
||||
{ "abp030pd", ABP030PD },
|
||||
{ "abp060pd", ABP060PD },
|
||||
{ /* empty */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, abp060mg_id_table);
|
||||
|
||||
static struct i2c_driver abp060mg_driver = {
|
||||
.driver = {
|
||||
.name = "abp060mg",
|
||||
},
|
||||
.probe = abp060mg_probe,
|
||||
.id_table = abp060mg_id_table,
|
||||
};
|
||||
module_i2c_driver(abp060mg_driver);
|
||||
|
||||
MODULE_AUTHOR("Marcin Malagowski <mrc@bourne.st>");
|
||||
MODULE_DESCRIPTION("Honeywell ABP pressure sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -82,8 +82,9 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE: /* in 0.25 pascal / LSB */
|
||||
@ -91,32 +92,39 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev,
|
||||
ret = mpl3115_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MPL3115_OUT_PRESS, 3, (u8 *) &tmp);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
*val = be32_to_cpu(tmp) >> 12;
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_TEMP: /* in 0.0625 celsius / LSB */
|
||||
mutex_lock(&data->lock);
|
||||
ret = mpl3115_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MPL3115_OUT_TEMP, 2, (u8 *) &tmp);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
*val = sign_extend32(be32_to_cpu(tmp) >> 20, 11);
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
|
@ -392,17 +392,14 @@ static int ms5611_init(struct iio_dev *indio_dev)
|
||||
|
||||
/* Enable attached regulator if any. */
|
||||
st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
|
||||
if (!IS_ERR(st->vdd)) {
|
||||
ret = regulator_enable(st->vdd);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to enable Vdd supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = PTR_ERR(st->vdd);
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
if (IS_ERR(st->vdd))
|
||||
return PTR_ERR(st->vdd);
|
||||
|
||||
ret = regulator_enable(st->vdd);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to enable Vdd supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ms5611_reset(indio_dev);
|
||||
|
@ -147,12 +147,8 @@ struct zpa2326_private {
|
||||
#define zpa2326_warn(_idev, _format, _arg...) \
|
||||
dev_warn(_idev->dev.parent, _format, ##_arg)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define zpa2326_dbg(_idev, _format, _arg...) \
|
||||
dev_dbg(_idev->dev.parent, _format, ##_arg)
|
||||
#else
|
||||
#define zpa2326_dbg(_idev, _format, _arg...)
|
||||
#endif
|
||||
|
||||
bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
|
@ -301,8 +301,6 @@ static int lidar_probe(struct i2c_client *client,
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
pm_runtime_enable(&client->dev);
|
||||
|
||||
pm_runtime_mark_last_busy(&client->dev);
|
||||
pm_runtime_idle(&client->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -1,76 +1,8 @@
|
||||
2009 8/18
|
||||
|
||||
Core:
|
||||
1) Get reviews
|
||||
2) Additional testing
|
||||
3) Ensure all desirable features present by adding more devices.
|
||||
Major changes not expected except in response to comments
|
||||
|
||||
Max1363 core:
|
||||
1) Possibly add sysfs exports of constant useful to userspace.
|
||||
Would be nice
|
||||
2) Support hardware generated interrupts
|
||||
3) Expand device set. Lots of other maxim adc's have very
|
||||
similar interfaces.
|
||||
|
||||
MXS LRADC driver:
|
||||
This is a classic MFD device as it combines the following subdevices
|
||||
- touchscreen controller (input subsystem related device)
|
||||
- general purpose ADC channels
|
||||
- battery voltage monitor (power subsystem related device)
|
||||
- die temperature monitor (thermal management)
|
||||
|
||||
At least the battery voltage and die temperature feature is required in-kernel
|
||||
by a driver of the SoC's battery charging unit to avoid any damage to the
|
||||
silicon and the battery.
|
||||
|
||||
TSL2561
|
||||
Would be nice
|
||||
1) Open question of userspace vs kernel space balance when
|
||||
converting to useful light measurements from device ones.
|
||||
2) Add sysfs elements necessary to allow device agnostic
|
||||
unit conversion.
|
||||
|
||||
LIS3L02DQ core
|
||||
|
||||
LIS3L02DQ ring
|
||||
|
||||
KXSD9
|
||||
Currently minimal driver, would be nice to add:
|
||||
1) Support for all chip generated interrupts (events),
|
||||
basically get support up to level of lis3l02dq driver.
|
||||
|
||||
Ring buffer core
|
||||
|
||||
SCA3000
|
||||
Would be nice
|
||||
1) Testing on devices other than sca3000-e05
|
||||
|
||||
Trigger core support
|
||||
1) Discussion of approach. Is it general enough?
|
||||
|
||||
Ring Buffer:
|
||||
1) Discussion of approach.
|
||||
There are probably better ways of doing this. The
|
||||
intention is to allow for more than one software ring
|
||||
buffer implementation as different users will have
|
||||
different requirements. This one suits mid range
|
||||
frequencies (100Hz - 4kHz).
|
||||
2) Lots of testing
|
||||
|
||||
GPIO trigger
|
||||
1) Add control over the type of interrupt etc. This will
|
||||
necessitate a header that is also visible from arch board
|
||||
files. (avoided at the moment to keep the driver set
|
||||
contained in staging).
|
||||
2016 10/09
|
||||
|
||||
ADI Drivers:
|
||||
CC the device-drivers-devel@blackfin.uclinux.org mailing list when
|
||||
e-mailing the normal IIO list (see below).
|
||||
|
||||
Documentation
|
||||
1) Lots of cleanup and expansion.
|
||||
2) Some device require individual docs.
|
||||
|
||||
Contact: Jonathan Cameron <jic23@kernel.org>.
|
||||
Mailing list: linux-iio@vger.kernel.org
|
||||
|
@ -51,14 +51,4 @@ config ADIS16240
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called adis16240.
|
||||
|
||||
config SCA3000
|
||||
depends on IIO_BUFFER
|
||||
depends on SPI
|
||||
tristate "VTI SCA3000 series accelerometers"
|
||||
help
|
||||
Say Y here to build support for the VTI SCA3000 series of SPI
|
||||
accelerometers. These devices use a hardware ring buffer.
|
||||
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called sca3000.
|
||||
endmenu
|
||||
|
@ -13,6 +13,3 @@ obj-$(CONFIG_ADIS16209) += adis16209.o
|
||||
|
||||
adis16240-y := adis16240_core.o
|
||||
obj-$(CONFIG_ADIS16240) += adis16240.o
|
||||
|
||||
sca3000-y := sca3000_core.o sca3000_ring.o
|
||||
obj-$(CONFIG_SCA3000) += sca3000.o
|
||||
|
@ -1,279 +0,0 @@
|
||||
/*
|
||||
* sca3000.c -- support VTI sca3000 series accelerometers
|
||||
* via SPI
|
||||
*
|
||||
* Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
|
||||
*
|
||||
* Partly based upon tle62x0.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Initial mode is direct measurement.
|
||||
*
|
||||
* Untested things
|
||||
*
|
||||
* Temperature reading (the e05 I'm testing with doesn't have a sensor)
|
||||
*
|
||||
* Free fall detection mode - supported but untested as I'm not droping my
|
||||
* dubious wire rig far enough to test it.
|
||||
*
|
||||
* Unsupported as yet
|
||||
*
|
||||
* Time stamping of data from ring. Various ideas on how to do this but none
|
||||
* are remotely simple. Suggestions welcome.
|
||||
*
|
||||
* Individual enabling disabling of channels going into ring buffer
|
||||
*
|
||||
* Overflow handling (this is signaled for all but 8 bit ring buffer mode.)
|
||||
*
|
||||
* Motion detector using AND combinations of signals.
|
||||
*
|
||||
* Note: Be very careful about not touching an register bytes marked
|
||||
* as reserved on the data sheet. They really mean it as changing convents of
|
||||
* some will cause the device to lock up.
|
||||
*
|
||||
* Known issues - on rare occasions the interrupts lock up. Not sure why as yet.
|
||||
* Can probably alleviate this by reading the interrupt register on start, but
|
||||
* that is really just brushing the problem under the carpet.
|
||||
*/
|
||||
#ifndef _SCA3000
|
||||
#define _SCA3000
|
||||
|
||||
#define SCA3000_WRITE_REG(a) (((a) << 2) | 0x02)
|
||||
#define SCA3000_READ_REG(a) ((a) << 2)
|
||||
|
||||
#define SCA3000_REG_ADDR_REVID 0x00
|
||||
#define SCA3000_REVID_MAJOR_MASK 0xf0
|
||||
#define SCA3000_REVID_MINOR_MASK 0x0f
|
||||
|
||||
#define SCA3000_REG_ADDR_STATUS 0x02
|
||||
#define SCA3000_LOCKED 0x20
|
||||
#define SCA3000_EEPROM_CS_ERROR 0x02
|
||||
#define SCA3000_SPI_FRAME_ERROR 0x01
|
||||
|
||||
/* All reads done using register decrement so no need to directly access LSBs */
|
||||
#define SCA3000_REG_ADDR_X_MSB 0x05
|
||||
#define SCA3000_REG_ADDR_Y_MSB 0x07
|
||||
#define SCA3000_REG_ADDR_Z_MSB 0x09
|
||||
|
||||
#define SCA3000_REG_ADDR_RING_OUT 0x0f
|
||||
|
||||
/* Temp read untested - the e05 doesn't have the sensor */
|
||||
#define SCA3000_REG_ADDR_TEMP_MSB 0x13
|
||||
|
||||
#define SCA3000_REG_ADDR_MODE 0x14
|
||||
#define SCA3000_MODE_PROT_MASK 0x28
|
||||
|
||||
#define SCA3000_RING_BUF_ENABLE 0x80
|
||||
#define SCA3000_RING_BUF_8BIT 0x40
|
||||
/*
|
||||
* Free fall detection triggers an interrupt if the acceleration
|
||||
* is below a threshold for equivalent of 25cm drop
|
||||
*/
|
||||
#define SCA3000_FREE_FALL_DETECT 0x10
|
||||
#define SCA3000_MEAS_MODE_NORMAL 0x00
|
||||
#define SCA3000_MEAS_MODE_OP_1 0x01
|
||||
#define SCA3000_MEAS_MODE_OP_2 0x02
|
||||
|
||||
/*
|
||||
* In motion detection mode the accelerations are band pass filtered
|
||||
* (approx 1 - 25Hz) and then a programmable threshold used to trigger
|
||||
* and interrupt.
|
||||
*/
|
||||
#define SCA3000_MEAS_MODE_MOT_DET 0x03
|
||||
|
||||
#define SCA3000_REG_ADDR_BUF_COUNT 0x15
|
||||
|
||||
#define SCA3000_REG_ADDR_INT_STATUS 0x16
|
||||
|
||||
#define SCA3000_INT_STATUS_THREE_QUARTERS 0x80
|
||||
#define SCA3000_INT_STATUS_HALF 0x40
|
||||
|
||||
#define SCA3000_INT_STATUS_FREE_FALL 0x08
|
||||
#define SCA3000_INT_STATUS_Y_TRIGGER 0x04
|
||||
#define SCA3000_INT_STATUS_X_TRIGGER 0x02
|
||||
#define SCA3000_INT_STATUS_Z_TRIGGER 0x01
|
||||
|
||||
/* Used to allow access to multiplexed registers */
|
||||
#define SCA3000_REG_ADDR_CTRL_SEL 0x18
|
||||
/* Only available for SCA3000-D03 and SCA3000-D01 */
|
||||
#define SCA3000_REG_CTRL_SEL_I2C_DISABLE 0x01
|
||||
#define SCA3000_REG_CTRL_SEL_MD_CTRL 0x02
|
||||
#define SCA3000_REG_CTRL_SEL_MD_Y_TH 0x03
|
||||
#define SCA3000_REG_CTRL_SEL_MD_X_TH 0x04
|
||||
#define SCA3000_REG_CTRL_SEL_MD_Z_TH 0x05
|
||||
/*
|
||||
* BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device
|
||||
* will not function
|
||||
*/
|
||||
#define SCA3000_REG_CTRL_SEL_OUT_CTRL 0x0B
|
||||
#define SCA3000_OUT_CTRL_PROT_MASK 0xE0
|
||||
#define SCA3000_OUT_CTRL_BUF_X_EN 0x10
|
||||
#define SCA3000_OUT_CTRL_BUF_Y_EN 0x08
|
||||
#define SCA3000_OUT_CTRL_BUF_Z_EN 0x04
|
||||
#define SCA3000_OUT_CTRL_BUF_DIV_MASK 0x03
|
||||
#define SCA3000_OUT_CTRL_BUF_DIV_4 0x02
|
||||
#define SCA3000_OUT_CTRL_BUF_DIV_2 0x01
|
||||
|
||||
/*
|
||||
* Control which motion detector interrupts are on.
|
||||
* For now only OR combinations are supported.
|
||||
*/
|
||||
#define SCA3000_MD_CTRL_PROT_MASK 0xC0
|
||||
#define SCA3000_MD_CTRL_OR_Y 0x01
|
||||
#define SCA3000_MD_CTRL_OR_X 0x02
|
||||
#define SCA3000_MD_CTRL_OR_Z 0x04
|
||||
/* Currently unsupported */
|
||||
#define SCA3000_MD_CTRL_AND_Y 0x08
|
||||
#define SCA3000_MD_CTRL_AND_X 0x10
|
||||
#define SAC3000_MD_CTRL_AND_Z 0x20
|
||||
|
||||
/*
|
||||
* Some control registers of complex access methods requiring this register to
|
||||
* be used to remove a lock.
|
||||
*/
|
||||
#define SCA3000_REG_ADDR_UNLOCK 0x1e
|
||||
|
||||
#define SCA3000_REG_ADDR_INT_MASK 0x21
|
||||
#define SCA3000_INT_MASK_PROT_MASK 0x1C
|
||||
|
||||
#define SCA3000_INT_MASK_RING_THREE_QUARTER 0x80
|
||||
#define SCA3000_INT_MASK_RING_HALF 0x40
|
||||
|
||||
#define SCA3000_INT_MASK_ALL_INTS 0x02
|
||||
#define SCA3000_INT_MASK_ACTIVE_HIGH 0x01
|
||||
#define SCA3000_INT_MASK_ACTIVE_LOW 0x00
|
||||
|
||||
/* Values of multiplexed registers (write to ctrl_data after select) */
|
||||
#define SCA3000_REG_ADDR_CTRL_DATA 0x22
|
||||
|
||||
/*
|
||||
* Measurement modes available on some sca3000 series chips. Code assumes others
|
||||
* may become available in the future.
|
||||
*
|
||||
* Bypass - Bypass the low-pass filter in the signal channel so as to increase
|
||||
* signal bandwidth.
|
||||
*
|
||||
* Narrow - Narrow low-pass filtering of the signal channel and half output
|
||||
* data rate by decimation.
|
||||
*
|
||||
* Wide - Widen low-pass filtering of signal channel to increase bandwidth
|
||||
*/
|
||||
#define SCA3000_OP_MODE_BYPASS 0x01
|
||||
#define SCA3000_OP_MODE_NARROW 0x02
|
||||
#define SCA3000_OP_MODE_WIDE 0x04
|
||||
#define SCA3000_MAX_TX 6
|
||||
#define SCA3000_MAX_RX 2
|
||||
|
||||
/**
|
||||
* struct sca3000_state - device instance state information
|
||||
* @us: the associated spi device
|
||||
* @info: chip variant information
|
||||
* @interrupt_handler_ws: event interrupt handler for all events
|
||||
* @last_timestamp: the timestamp of the last event
|
||||
* @mo_det_use_count: reference counter for the motion detection unit
|
||||
* @lock: lock used to protect elements of sca3000_state
|
||||
* and the underlying device state.
|
||||
* @bpse: number of bits per scan element
|
||||
* @tx: dma-able transmit buffer
|
||||
* @rx: dma-able receive buffer
|
||||
**/
|
||||
struct sca3000_state {
|
||||
struct spi_device *us;
|
||||
const struct sca3000_chip_info *info;
|
||||
struct work_struct interrupt_handler_ws;
|
||||
s64 last_timestamp;
|
||||
int mo_det_use_count;
|
||||
struct mutex lock;
|
||||
int bpse;
|
||||
/* Can these share a cacheline ? */
|
||||
u8 rx[2] ____cacheline_aligned;
|
||||
u8 tx[6] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sca3000_chip_info - model dependent parameters
|
||||
* @scale: scale * 10^-6
|
||||
* @temp_output: some devices have temperature sensors.
|
||||
* @measurement_mode_freq: normal mode sampling frequency
|
||||
* @option_mode_1: first optional mode. Not all models have one
|
||||
* @option_mode_1_freq: option mode 1 sampling frequency
|
||||
* @option_mode_2: second optional mode. Not all chips have one
|
||||
* @option_mode_2_freq: option mode 2 sampling frequency
|
||||
*
|
||||
* This structure is used to hold information about the functionality of a given
|
||||
* sca3000 variant.
|
||||
**/
|
||||
struct sca3000_chip_info {
|
||||
unsigned int scale;
|
||||
bool temp_output;
|
||||
int measurement_mode_freq;
|
||||
int option_mode_1;
|
||||
int option_mode_1_freq;
|
||||
int option_mode_2;
|
||||
int option_mode_2_freq;
|
||||
int mot_det_mult_xz[6];
|
||||
int mot_det_mult_y[7];
|
||||
};
|
||||
|
||||
int sca3000_read_data_short(struct sca3000_state *st,
|
||||
u8 reg_address_high,
|
||||
int len);
|
||||
|
||||
/**
|
||||
* sca3000_write_reg() write a single register
|
||||
* @address: address of register on chip
|
||||
* @val: value to be written to register
|
||||
*
|
||||
* The main lock must be held.
|
||||
**/
|
||||
int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
/**
|
||||
* sca3000_register_ring_funcs() setup the ring state change functions
|
||||
**/
|
||||
void sca3000_register_ring_funcs(struct iio_dev *indio_dev);
|
||||
|
||||
/**
|
||||
* sca3000_configure_ring() - allocate and configure ring buffer
|
||||
* @indio_dev: iio-core device whose ring is to be configured
|
||||
*
|
||||
* The hardware ring buffer needs far fewer ring buffer functions than
|
||||
* a software one as a lot of things are handled automatically.
|
||||
* This function also tells the iio core that our device supports a
|
||||
* hardware ring buffer mode.
|
||||
**/
|
||||
int sca3000_configure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
/**
|
||||
* sca3000_unconfigure_ring() - deallocate the ring buffer
|
||||
* @indio_dev: iio-core device whose ring we are freeing
|
||||
**/
|
||||
void sca3000_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
/**
|
||||
* sca3000_ring_int_process() handles ring related event pushing and escalation
|
||||
* @val: the event code
|
||||
**/
|
||||
void sca3000_ring_int_process(u8 val, struct iio_buffer *ring);
|
||||
|
||||
#else
|
||||
static inline void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sca3000_ring_int_process(u8 val, void *ring)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* _SCA3000 */
|
File diff suppressed because it is too large
Load Diff
@ -1,350 +0,0 @@
|
||||
/*
|
||||
* sca3000_ring.c -- support VTI sca3000 series accelerometers via SPI
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include "../ring_hw.h"
|
||||
#include "sca3000.h"
|
||||
|
||||
/* RFC / future work
|
||||
*
|
||||
* The internal ring buffer doesn't actually change what it holds depending
|
||||
* on which signals are enabled etc, merely whether you can read them.
|
||||
* As such the scan mode selection is somewhat different than for a software
|
||||
* ring buffer and changing it actually covers any data already in the buffer.
|
||||
* Currently scan elements aren't configured so it doesn't matter.
|
||||
*/
|
||||
|
||||
static int sca3000_read_data(struct sca3000_state *st,
|
||||
u8 reg_address_high,
|
||||
u8 **rx_p,
|
||||
int len)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer xfer[2] = {
|
||||
{
|
||||
.len = 1,
|
||||
.tx_buf = st->tx,
|
||||
}, {
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
*rx_p = kmalloc(len, GFP_KERNEL);
|
||||
if (!*rx_p) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
xfer[1].rx_buf = *rx_p;
|
||||
st->tx[0] = SCA3000_READ_REG(reg_address_high);
|
||||
ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
|
||||
if (ret) {
|
||||
dev_err(get_device(&st->us->dev), "problem reading register");
|
||||
goto error_free_rx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_free_rx:
|
||||
kfree(*rx_p);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sca3000_read_first_n_hw_rb() - main ring access, pulls data from ring
|
||||
* @r: the ring
|
||||
* @count: number of samples to try and pull
|
||||
* @data: output the actual samples pulled from the hw ring
|
||||
*
|
||||
* Currently does not provide timestamps. As the hardware doesn't add them they
|
||||
* can only be inferred approximately from ring buffer events such as 50% full
|
||||
* and knowledge of when buffer was last emptied. This is left to userspace.
|
||||
**/
|
||||
static int sca3000_read_first_n_hw_rb(struct iio_buffer *r,
|
||||
size_t count, char __user *buf)
|
||||
{
|
||||
struct iio_hw_buffer *hw_ring = iio_to_hw_buf(r);
|
||||
struct iio_dev *indio_dev = hw_ring->private;
|
||||
struct sca3000_state *st = iio_priv(indio_dev);
|
||||
u8 *rx;
|
||||
int ret, i, num_available, num_read = 0;
|
||||
int bytes_per_sample = 1;
|
||||
|
||||
if (st->bpse == 11)
|
||||
bytes_per_sample = 2;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (count % bytes_per_sample) {
|
||||
ret = -EINVAL;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_BUF_COUNT, 1);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
num_available = st->rx[0];
|
||||
/*
|
||||
* num_available is the total number of samples available
|
||||
* i.e. number of time points * number of channels.
|
||||
*/
|
||||
if (count > num_available * bytes_per_sample)
|
||||
num_read = num_available * bytes_per_sample;
|
||||
else
|
||||
num_read = count;
|
||||
|
||||
ret = sca3000_read_data(st,
|
||||
SCA3000_REG_ADDR_RING_OUT,
|
||||
&rx, num_read);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
for (i = 0; i < num_read / sizeof(u16); i++)
|
||||
*(((u16 *)rx) + i) = be16_to_cpup((__be16 *)rx + i);
|
||||
|
||||
if (copy_to_user(buf, rx, num_read))
|
||||
ret = -EFAULT;
|
||||
kfree(rx);
|
||||
r->stufftoread = 0;
|
||||
error_ret:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret ? ret : num_read;
|
||||
}
|
||||
|
||||
static size_t sca3000_ring_buf_data_available(struct iio_buffer *r)
|
||||
{
|
||||
return r->stufftoread ? r->watermark : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sca3000_query_ring_int() is the hardware ring status interrupt enabled
|
||||
**/
|
||||
static ssize_t sca3000_query_ring_int(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int ret, val;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct sca3000_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
|
||||
val = st->rx[0];
|
||||
mutex_unlock(&st->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", !!(val & this_attr->address));
|
||||
}
|
||||
|
||||
/**
|
||||
* sca3000_set_ring_int() set state of ring status interrupt
|
||||
**/
|
||||
static ssize_t sca3000_set_ring_int(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct sca3000_state *st = iio_priv(indio_dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = kstrtou8(buf, 10, &val);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
if (val)
|
||||
ret = sca3000_write_reg(st,
|
||||
SCA3000_REG_ADDR_INT_MASK,
|
||||
st->rx[0] | this_attr->address);
|
||||
else
|
||||
ret = sca3000_write_reg(st,
|
||||
SCA3000_REG_ADDR_INT_MASK,
|
||||
st->rx[0] & ~this_attr->address);
|
||||
error_ret:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(50_percent, S_IRUGO | S_IWUSR,
|
||||
sca3000_query_ring_int,
|
||||
sca3000_set_ring_int,
|
||||
SCA3000_INT_MASK_RING_HALF);
|
||||
|
||||
static IIO_DEVICE_ATTR(75_percent, S_IRUGO | S_IWUSR,
|
||||
sca3000_query_ring_int,
|
||||
sca3000_set_ring_int,
|
||||
SCA3000_INT_MASK_RING_THREE_QUARTER);
|
||||
|
||||
static ssize_t sca3000_show_buffer_scale(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct sca3000_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "0.%06d\n", 4 * st->info->scale);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_accel_scale,
|
||||
S_IRUGO,
|
||||
sca3000_show_buffer_scale,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
/*
|
||||
* Ring buffer attributes
|
||||
* This device is a bit unusual in that the sampling frequency and bpse
|
||||
* only apply to the ring buffer. At all times full rate and accuracy
|
||||
* is available via direct reading from registers.
|
||||
*/
|
||||
static const struct attribute *sca3000_ring_attributes[] = {
|
||||
&iio_dev_attr_50_percent.dev_attr.attr,
|
||||
&iio_dev_attr_75_percent.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buf;
|
||||
struct iio_hw_buffer *ring;
|
||||
|
||||
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
|
||||
if (!ring)
|
||||
return NULL;
|
||||
|
||||
ring->private = indio_dev;
|
||||
buf = &ring->buf;
|
||||
buf->stufftoread = 0;
|
||||
buf->length = 64;
|
||||
buf->attrs = sca3000_ring_attributes;
|
||||
iio_buffer_init(buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void sca3000_ring_release(struct iio_buffer *r)
|
||||
{
|
||||
kfree(iio_to_hw_buf(r));
|
||||
}
|
||||
|
||||
static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = {
|
||||
.read_first_n = &sca3000_read_first_n_hw_rb,
|
||||
.data_available = sca3000_ring_buf_data_available,
|
||||
.release = sca3000_ring_release,
|
||||
|
||||
.modes = INDIO_BUFFER_HARDWARE,
|
||||
};
|
||||
|
||||
int sca3000_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
buffer = sca3000_rb_allocate(indio_dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
|
||||
|
||||
indio_dev->buffer->access = &sca3000_ring_access_funcs;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_buffer_put(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static inline
|
||||
int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
|
||||
{
|
||||
struct sca3000_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
if (state) {
|
||||
dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n");
|
||||
ret = sca3000_write_reg(st,
|
||||
SCA3000_REG_ADDR_MODE,
|
||||
(st->rx[0] | SCA3000_RING_BUF_ENABLE));
|
||||
} else
|
||||
ret = sca3000_write_reg(st,
|
||||
SCA3000_REG_ADDR_MODE,
|
||||
(st->rx[0] & ~SCA3000_RING_BUF_ENABLE));
|
||||
error_ret:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sca3000_hw_ring_preenable() hw ring buffer preenable function
|
||||
*
|
||||
* Very simple enable function as the chip will allows normal reads
|
||||
* during ring buffer operation so as long as it is indeed running
|
||||
* before we notify the core, the precise ordering does not matter.
|
||||
**/
|
||||
static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return __sca3000_hw_ring_state_set(indio_dev, 1);
|
||||
}
|
||||
|
||||
static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return __sca3000_hw_ring_state_set(indio_dev, 0);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = {
|
||||
.preenable = &sca3000_hw_ring_preenable,
|
||||
.postdisable = &sca3000_hw_ring_postdisable,
|
||||
};
|
||||
|
||||
void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
|
||||
{
|
||||
indio_dev->setup_ops = &sca3000_ring_setup_ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* sca3000_ring_int_process() ring specific interrupt handling.
|
||||
*
|
||||
* This is only split from the main interrupt handler so as to
|
||||
* reduce the amount of code if the ring buffer is not enabled.
|
||||
**/
|
||||
void sca3000_ring_int_process(u8 val, struct iio_buffer *ring)
|
||||
{
|
||||
if (val & (SCA3000_INT_STATUS_THREE_QUARTERS |
|
||||
SCA3000_INT_STATUS_HALF)) {
|
||||
ring->stufftoread = true;
|
||||
wake_up_interruptible(&ring->pollq);
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
# Makefile for industrial I/O ADC drivers
|
||||
#
|
||||
|
||||
ad7606-y := ad7606_core.o ad7606_ring.o
|
||||
obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
|
||||
obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
|
||||
obj-$(CONFIG_AD7606) += ad7606.o
|
||||
|
@ -322,57 +322,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad7192_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->mclk /
|
||||
(st->f_order * 1024 * AD7192_MODE_RATE(st->mode)));
|
||||
}
|
||||
|
||||
static ssize_t ad7192_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
unsigned long lval;
|
||||
int div, ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &lval);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (lval == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
div = st->mclk / (lval * st->f_order * 1024);
|
||||
if (div < 1 || div > 1023) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
st->mode &= ~AD7192_MODE_RATE(-1);
|
||||
st->mode |= AD7192_MODE_RATE(div);
|
||||
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad7192_read_frequency,
|
||||
ad7192_write_frequency);
|
||||
|
||||
static ssize_t
|
||||
ad7192_show_scale_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -471,7 +420,6 @@ static IIO_DEVICE_ATTR(ac_excitation_en, S_IRUGO | S_IWUSR,
|
||||
AD7192_REG_MODE);
|
||||
|
||||
static struct attribute *ad7192_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_bridge_switch_en.dev_attr.attr,
|
||||
@ -484,7 +432,6 @@ static const struct attribute_group ad7192_attribute_group = {
|
||||
};
|
||||
|
||||
static struct attribute *ad7195_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_bridge_switch_en.dev_attr.attr,
|
||||
@ -536,6 +483,10 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
|
||||
if (chan->type == IIO_TEMP)
|
||||
*val -= 273 * ad7192_get_temp_scale(unipolar);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = st->mclk /
|
||||
(st->f_order * 1024 * AD7192_MODE_RATE(st->mode));
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@ -548,7 +499,7 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
|
||||
long mask)
|
||||
{
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
int ret, i, div;
|
||||
unsigned int tmp;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
@ -572,6 +523,22 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (!val) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
div = st->mclk / (val * st->f_order * 1024);
|
||||
if (div < 1 || div > 1023) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
st->mode &= ~AD7192_MODE_RATE(-1);
|
||||
st->mode |= AD7192_MODE_RATE(div);
|
||||
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
@ -585,7 +552,14 @@ static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad7192_info = {
|
||||
|
@ -777,7 +777,7 @@ static struct attribute *ad7280_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad7280_event_attrs_group = {
|
||||
static const struct attribute_group ad7280_event_attrs_group = {
|
||||
.attrs = ad7280_event_attributes,
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
@ -21,58 +21,109 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include "ad7606.h"
|
||||
|
||||
int ad7606_reset(struct ad7606_state *st)
|
||||
static int ad7606_reset(struct ad7606_state *st)
|
||||
{
|
||||
if (gpio_is_valid(st->pdata->gpio_reset)) {
|
||||
gpio_set_value(st->pdata->gpio_reset, 1);
|
||||
if (st->gpio_reset) {
|
||||
gpiod_set_value(st->gpio_reset, 1);
|
||||
ndelay(100); /* t_reset >= 100ns */
|
||||
gpio_set_value(st->pdata->gpio_reset, 0);
|
||||
gpiod_set_value(st->gpio_reset, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int ad7606_read_samples(struct ad7606_state *st)
|
||||
{
|
||||
unsigned int num = st->chip_info->num_channels;
|
||||
u16 *data = st->data;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The frstdata signal is set to high while and after reading the sample
|
||||
* of the first channel and low for all other channels. This can be used
|
||||
* to check that the incoming data is correctly aligned. During normal
|
||||
* operation the data should never become unaligned, but some glitch or
|
||||
* electrostatic discharge might cause an extra read or clock cycle.
|
||||
* Monitoring the frstdata signal allows to recover from such failure
|
||||
* situations.
|
||||
*/
|
||||
|
||||
if (st->gpio_frstdata) {
|
||||
ret = st->bops->read_block(st->dev, 1, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!gpiod_get_value(st->gpio_frstdata)) {
|
||||
ad7606_reset(st);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data++;
|
||||
num--;
|
||||
}
|
||||
|
||||
return st->bops->read_block(st->dev, num, data);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7606_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct ad7606_state *st = iio_priv(pf->indio_dev);
|
||||
|
||||
gpiod_set_value(st->gpio_convst, 1);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer
|
||||
* @work_s: the work struct through which this was scheduled
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
* I think the one copy of this at a time was to avoid problems if the
|
||||
* trigger was set far too high and the reads then locked up the computer.
|
||||
**/
|
||||
static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
|
||||
{
|
||||
struct ad7606_state *st = container_of(work_s, struct ad7606_state,
|
||||
poll_work);
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(st);
|
||||
int ret;
|
||||
|
||||
ret = ad7606_read_samples(st);
|
||||
if (ret == 0)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
gpiod_set_value(st->gpio_convst, 0);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
}
|
||||
|
||||
static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
st->done = false;
|
||||
gpio_set_value(st->pdata->gpio_convst, 1);
|
||||
gpiod_set_value(st->gpio_convst, 1);
|
||||
|
||||
ret = wait_event_interruptible(st->wq_data_avail, st->done);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_frstdata)) {
|
||||
ret = st->bops->read_block(st->dev, 1, st->data);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
if (!gpio_get_value(st->pdata->gpio_frstdata)) {
|
||||
/* This should never happen */
|
||||
ad7606_reset(st);
|
||||
ret = -EIO;
|
||||
goto error_ret;
|
||||
}
|
||||
ret = st->bops->read_block(st->dev,
|
||||
st->chip_info->num_channels - 1, &st->data[1]);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
} else {
|
||||
ret = st->bops->read_block(st->dev,
|
||||
st->chip_info->num_channels, st->data);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = st->data[ch];
|
||||
ret = ad7606_read_samples(st);
|
||||
if (ret == 0)
|
||||
ret = st->data[ch];
|
||||
|
||||
error_ret:
|
||||
gpio_set_value(st->pdata->gpio_convst, 0);
|
||||
gpiod_set_value(st->gpio_convst, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -103,6 +154,9 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
||||
*val = st->range * 2;
|
||||
*val2 = st->chip_info->channels[0].scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*val = st->oversampling;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -129,12 +183,11 @@ static ssize_t ad7606_store_range(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(lval == 5000 || lval == 10000)) {
|
||||
dev_err(dev, "range is not supported\n");
|
||||
if (!(lval == 5000 || lval == 10000))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
gpio_set_value(st->pdata->gpio_range, lval == 10000);
|
||||
gpiod_set_value(st->gpio_range, lval == 10000);
|
||||
st->range = lval;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
@ -145,19 +198,9 @@ static IIO_DEVICE_ATTR(in_voltage_range, S_IRUGO | S_IWUSR,
|
||||
ad7606_show_range, ad7606_store_range, 0);
|
||||
static IIO_CONST_ATTR(in_voltage_range_available, "5000 10000");
|
||||
|
||||
static ssize_t ad7606_show_oversampling_ratio(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%u\n", st->oversampling);
|
||||
}
|
||||
|
||||
static int ad7606_oversampling_get_index(unsigned int val)
|
||||
{
|
||||
unsigned char supported[] = {0, 2, 4, 8, 16, 32, 64};
|
||||
unsigned char supported[] = {1, 2, 4, 8, 16, 32, 64};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(supported); i++)
|
||||
@ -167,44 +210,45 @@ static int ad7606_oversampling_get_index(unsigned int val)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t ad7606_store_oversampling_ratio(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static int ad7606_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
unsigned long lval;
|
||||
int values[3];
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &lval);
|
||||
if (ret)
|
||||
return ret;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
ret = ad7606_oversampling_get_index(val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ad7606_oversampling_get_index(lval);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "oversampling %lu is not supported\n", lval);
|
||||
return ret;
|
||||
values[0] = (ret >> 0) & 1;
|
||||
values[1] = (ret >> 1) & 1;
|
||||
values[2] = (ret >> 2) & 1;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
|
||||
values);
|
||||
st->oversampling = val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1);
|
||||
gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1);
|
||||
gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1);
|
||||
st->oversampling = lval;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(oversampling_ratio, S_IRUGO | S_IWUSR,
|
||||
ad7606_show_oversampling_ratio,
|
||||
ad7606_store_oversampling_ratio, 0);
|
||||
static IIO_CONST_ATTR(oversampling_ratio_available, "0 2 4 8 16 32 64");
|
||||
static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64");
|
||||
|
||||
static struct attribute *ad7606_attributes_os_and_range[] = {
|
||||
&iio_dev_attr_in_voltage_range.dev_attr.attr,
|
||||
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
|
||||
&iio_dev_attr_oversampling_ratio.dev_attr.attr,
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
@ -214,7 +258,6 @@ static const struct attribute_group ad7606_attribute_group_os_and_range = {
|
||||
};
|
||||
|
||||
static struct attribute *ad7606_attributes_os[] = {
|
||||
&iio_dev_attr_oversampling_ratio.dev_attr.attr,
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
@ -241,6 +284,8 @@ static const struct attribute_group ad7606_attribute_group_range = {
|
||||
.address = num, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.info_mask_shared_by_all = \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
@ -267,20 +312,14 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
|
||||
* More devices added in future
|
||||
*/
|
||||
[ID_AD7606_8] = {
|
||||
.name = "ad7606",
|
||||
.int_vref_mv = 2500,
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 9,
|
||||
},
|
||||
[ID_AD7606_6] = {
|
||||
.name = "ad7606-6",
|
||||
.int_vref_mv = 2500,
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 7,
|
||||
},
|
||||
[ID_AD7606_4] = {
|
||||
.name = "ad7606-4",
|
||||
.int_vref_mv = 2500,
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 5,
|
||||
},
|
||||
@ -288,119 +327,34 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
|
||||
|
||||
static int ad7606_request_gpios(struct ad7606_state *st)
|
||||
{
|
||||
struct gpio gpio_array[3] = {
|
||||
[0] = {
|
||||
.gpio = st->pdata->gpio_os0,
|
||||
.flags = GPIOF_DIR_OUT | ((st->oversampling & 1) ?
|
||||
GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
|
||||
.label = "AD7606_OS0",
|
||||
},
|
||||
[1] = {
|
||||
.gpio = st->pdata->gpio_os1,
|
||||
.flags = GPIOF_DIR_OUT | ((st->oversampling & 2) ?
|
||||
GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
|
||||
.label = "AD7606_OS1",
|
||||
},
|
||||
[2] = {
|
||||
.gpio = st->pdata->gpio_os2,
|
||||
.flags = GPIOF_DIR_OUT | ((st->oversampling & 4) ?
|
||||
GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
|
||||
.label = "AD7606_OS2",
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
struct device *dev = st->dev;
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_convst)) {
|
||||
ret = gpio_request_one(st->pdata->gpio_convst,
|
||||
GPIOF_OUT_INIT_LOW,
|
||||
"AD7606_CONVST");
|
||||
if (ret) {
|
||||
dev_err(st->dev, "failed to request GPIO CONVST\n");
|
||||
goto error_ret;
|
||||
}
|
||||
} else {
|
||||
ret = -EIO;
|
||||
goto error_ret;
|
||||
}
|
||||
st->gpio_convst = devm_gpiod_get(dev, "conversion-start",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_convst))
|
||||
return PTR_ERR(st->gpio_convst);
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_os0) &&
|
||||
gpio_is_valid(st->pdata->gpio_os1) &&
|
||||
gpio_is_valid(st->pdata->gpio_os2)) {
|
||||
ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array));
|
||||
if (ret < 0)
|
||||
goto error_free_convst;
|
||||
}
|
||||
st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_reset))
|
||||
return PTR_ERR(st->gpio_reset);
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_reset)) {
|
||||
ret = gpio_request_one(st->pdata->gpio_reset,
|
||||
GPIOF_OUT_INIT_LOW,
|
||||
"AD7606_RESET");
|
||||
if (ret < 0)
|
||||
goto error_free_os;
|
||||
}
|
||||
st->gpio_range = devm_gpiod_get_optional(dev, "range", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_range))
|
||||
return PTR_ERR(st->gpio_range);
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_range)) {
|
||||
ret = gpio_request_one(st->pdata->gpio_range, GPIOF_DIR_OUT |
|
||||
((st->range == 10000) ? GPIOF_INIT_HIGH :
|
||||
GPIOF_INIT_LOW), "AD7606_RANGE");
|
||||
if (ret < 0)
|
||||
goto error_free_reset;
|
||||
}
|
||||
if (gpio_is_valid(st->pdata->gpio_stby)) {
|
||||
ret = gpio_request_one(st->pdata->gpio_stby,
|
||||
GPIOF_OUT_INIT_HIGH,
|
||||
"AD7606_STBY");
|
||||
if (ret < 0)
|
||||
goto error_free_range;
|
||||
}
|
||||
st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->gpio_standby))
|
||||
return PTR_ERR(st->gpio_standby);
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_frstdata)) {
|
||||
ret = gpio_request_one(st->pdata->gpio_frstdata, GPIOF_IN,
|
||||
"AD7606_FRSTDATA");
|
||||
if (ret < 0)
|
||||
goto error_free_stby;
|
||||
}
|
||||
st->gpio_frstdata = devm_gpiod_get_optional(dev, "first-data",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(st->gpio_frstdata))
|
||||
return PTR_ERR(st->gpio_frstdata);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_stby:
|
||||
if (gpio_is_valid(st->pdata->gpio_stby))
|
||||
gpio_free(st->pdata->gpio_stby);
|
||||
error_free_range:
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
gpio_free(st->pdata->gpio_range);
|
||||
error_free_reset:
|
||||
if (gpio_is_valid(st->pdata->gpio_reset))
|
||||
gpio_free(st->pdata->gpio_reset);
|
||||
error_free_os:
|
||||
if (gpio_is_valid(st->pdata->gpio_os0) &&
|
||||
gpio_is_valid(st->pdata->gpio_os1) &&
|
||||
gpio_is_valid(st->pdata->gpio_os2))
|
||||
gpio_free_array(gpio_array, ARRAY_SIZE(gpio_array));
|
||||
error_free_convst:
|
||||
gpio_free(st->pdata->gpio_convst);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ad7606_free_gpios(struct ad7606_state *st)
|
||||
{
|
||||
if (gpio_is_valid(st->pdata->gpio_frstdata))
|
||||
gpio_free(st->pdata->gpio_frstdata);
|
||||
if (gpio_is_valid(st->pdata->gpio_stby))
|
||||
gpio_free(st->pdata->gpio_stby);
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
gpio_free(st->pdata->gpio_range);
|
||||
if (gpio_is_valid(st->pdata->gpio_reset))
|
||||
gpio_free(st->pdata->gpio_reset);
|
||||
if (gpio_is_valid(st->pdata->gpio_os0) &&
|
||||
gpio_is_valid(st->pdata->gpio_os1) &&
|
||||
gpio_is_valid(st->pdata->gpio_os2)) {
|
||||
gpio_free(st->pdata->gpio_os2);
|
||||
gpio_free(st->pdata->gpio_os1);
|
||||
gpio_free(st->pdata->gpio_os0);
|
||||
}
|
||||
gpio_free(st->pdata->gpio_convst);
|
||||
st->gpio_os = devm_gpiod_get_array_optional(dev, "oversampling-ratio",
|
||||
GPIOD_OUT_LOW);
|
||||
return PTR_ERR_OR_ZERO(st->gpio_os);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -429,12 +383,14 @@ static const struct iio_info ad7606_info_no_os_or_range = {
|
||||
static const struct iio_info ad7606_info_os_and_range = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.attrs = &ad7606_attribute_group_os_and_range,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.attrs = &ad7606_attribute_group_os,
|
||||
};
|
||||
|
||||
@ -444,81 +400,73 @@ static const struct iio_info ad7606_info_range = {
|
||||
.attrs = &ad7606_attribute_group_range,
|
||||
};
|
||||
|
||||
struct iio_dev *ad7606_probe(struct device *dev, int irq,
|
||||
void __iomem *base_address,
|
||||
unsigned int id,
|
||||
const struct ad7606_bus_ops *bops)
|
||||
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
const char *name, unsigned int id,
|
||||
const struct ad7606_bus_ops *bops)
|
||||
{
|
||||
struct ad7606_platform_data *pdata = dev->platform_data;
|
||||
struct ad7606_state *st;
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->dev = dev;
|
||||
st->bops = bops;
|
||||
st->base_address = base_address;
|
||||
st->range = pdata->default_range == 10000 ? 10000 : 5000;
|
||||
st->range = 5000;
|
||||
st->oversampling = 1;
|
||||
INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
|
||||
|
||||
ret = ad7606_oversampling_get_index(pdata->default_os);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "oversampling %d is not supported\n",
|
||||
pdata->default_os);
|
||||
st->oversampling = 0;
|
||||
} else {
|
||||
st->oversampling = pdata->default_os;
|
||||
st->reg = devm_regulator_get(dev, "avcc");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable specified AVcc supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->reg = devm_regulator_get(dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
st->pdata = pdata;
|
||||
st->chip_info = &ad7606_chip_info_tbl[id];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
if (gpio_is_valid(st->pdata->gpio_os0) &&
|
||||
gpio_is_valid(st->pdata->gpio_os1) &&
|
||||
gpio_is_valid(st->pdata->gpio_os2)) {
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
indio_dev->info = &ad7606_info_os_and_range;
|
||||
else
|
||||
indio_dev->info = &ad7606_info_os;
|
||||
} else {
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
indio_dev->info = &ad7606_info_range;
|
||||
else
|
||||
indio_dev->info = &ad7606_info_no_os_or_range;
|
||||
}
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = st->chip_info->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
init_waitqueue_head(&st->wq_data_avail);
|
||||
|
||||
ret = ad7606_request_gpios(st);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->chip_info = &ad7606_chip_info_tbl[id];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
if (st->gpio_os) {
|
||||
if (st->gpio_range)
|
||||
indio_dev->info = &ad7606_info_os_and_range;
|
||||
else
|
||||
indio_dev->info = &ad7606_info_os;
|
||||
} else {
|
||||
if (st->gpio_range)
|
||||
indio_dev->info = &ad7606_info_range;
|
||||
else
|
||||
indio_dev->info = &ad7606_info_no_os_or_range;
|
||||
}
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
init_waitqueue_head(&st->wq_data_avail);
|
||||
|
||||
ret = ad7606_reset(st);
|
||||
if (ret)
|
||||
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
|
||||
|
||||
ret = request_irq(irq, ad7606_interrupt,
|
||||
IRQF_TRIGGER_FALLING, st->chip_info->name, indio_dev);
|
||||
ret = request_irq(irq, ad7606_interrupt, IRQF_TRIGGER_FALLING, name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_free_gpios;
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad7606_register_ring_funcs_and_init(indio_dev);
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &ad7606_trigger_handler,
|
||||
NULL, NULL);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
@ -526,35 +474,31 @@ struct iio_dev *ad7606_probe(struct device *dev, int irq,
|
||||
if (ret)
|
||||
goto error_unregister_ring;
|
||||
|
||||
return indio_dev;
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
return 0;
|
||||
error_unregister_ring:
|
||||
ad7606_ring_cleanup(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
error_free_irq:
|
||||
free_irq(irq, indio_dev);
|
||||
|
||||
error_free_gpios:
|
||||
ad7606_free_gpios(st);
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
return ERR_PTR(ret);
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad7606_probe);
|
||||
|
||||
int ad7606_remove(struct iio_dev *indio_dev, int irq)
|
||||
int ad7606_remove(struct device *dev, int irq)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad7606_ring_cleanup(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
free_irq(irq, indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
ad7606_free_gpios(st);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -567,10 +511,9 @@ static int ad7606_suspend(struct device *dev)
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_stby)) {
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
gpio_set_value(st->pdata->gpio_range, 1);
|
||||
gpio_set_value(st->pdata->gpio_stby, 0);
|
||||
if (st->gpio_standby) {
|
||||
gpiod_set_value(st->gpio_range, 1);
|
||||
gpiod_set_value(st->gpio_standby, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -581,12 +524,9 @@ static int ad7606_resume(struct device *dev)
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_stby)) {
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
gpio_set_value(st->pdata->gpio_range,
|
||||
st->range == 10000);
|
||||
|
||||
gpio_set_value(st->pdata->gpio_stby, 1);
|
||||
if (st->gpio_standby) {
|
||||
gpiod_set_value(st->gpio_range, st->range == 10000);
|
||||
gpiod_set_value(st->gpio_standby, 1);
|
||||
ad7606_reset(st);
|
||||
}
|
||||
|
@ -9,48 +9,14 @@
|
||||
#ifndef IIO_ADC_AD7606_H_
|
||||
#define IIO_ADC_AD7606_H_
|
||||
|
||||
/*
|
||||
* TODO: struct ad7606_platform_data needs to go into include/linux/iio
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ad7606_platform_data - platform/board specific information
|
||||
* @default_os: default oversampling value {0, 2, 4, 8, 16, 32, 64}
|
||||
* @default_range: default range +/-{5000, 10000} mVolt
|
||||
* @gpio_convst: number of gpio connected to the CONVST pin
|
||||
* @gpio_reset: gpio connected to the RESET pin, if not used set to -1
|
||||
* @gpio_range: gpio connected to the RANGE pin, if not used set to -1
|
||||
* @gpio_os0: gpio connected to the OS0 pin, if not used set to -1
|
||||
* @gpio_os1: gpio connected to the OS1 pin, if not used set to -1
|
||||
* @gpio_os2: gpio connected to the OS2 pin, if not used set to -1
|
||||
* @gpio_frstdata: gpio connected to the FRSTDAT pin, if not used set to -1
|
||||
* @gpio_stby: gpio connected to the STBY pin, if not used set to -1
|
||||
*/
|
||||
|
||||
struct ad7606_platform_data {
|
||||
unsigned int default_os;
|
||||
unsigned int default_range;
|
||||
unsigned int gpio_convst;
|
||||
unsigned int gpio_reset;
|
||||
unsigned int gpio_range;
|
||||
unsigned int gpio_os0;
|
||||
unsigned int gpio_os1;
|
||||
unsigned int gpio_os2;
|
||||
unsigned int gpio_frstdata;
|
||||
unsigned int gpio_stby;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad7606_chip_info - chip specific information
|
||||
* @name: identification string for chip
|
||||
* @int_vref_mv: the internal reference voltage
|
||||
* @channels: channel specification
|
||||
* @num_channels: number of channels
|
||||
*/
|
||||
|
||||
struct ad7606_chip_info {
|
||||
const char *name;
|
||||
u16 int_vref_mv;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
@ -62,7 +28,6 @@ struct ad7606_chip_info {
|
||||
struct ad7606_state {
|
||||
struct device *dev;
|
||||
const struct ad7606_chip_info *chip_info;
|
||||
struct ad7606_platform_data *pdata;
|
||||
struct regulator *reg;
|
||||
struct work_struct poll_work;
|
||||
wait_queue_head_t wq_data_avail;
|
||||
@ -72,12 +37,19 @@ struct ad7606_state {
|
||||
bool done;
|
||||
void __iomem *base_address;
|
||||
|
||||
struct gpio_desc *gpio_convst;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct gpio_desc *gpio_range;
|
||||
struct gpio_desc *gpio_standby;
|
||||
struct gpio_desc *gpio_frstdata;
|
||||
struct gpio_descs *gpio_os;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* 8 * 16-bit samples + 64-bit timestamp
|
||||
*/
|
||||
|
||||
unsigned short data[8] ____cacheline_aligned;
|
||||
unsigned short data[12] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct ad7606_bus_ops {
|
||||
@ -85,11 +57,10 @@ struct ad7606_bus_ops {
|
||||
int (*read_block)(struct device *, int, void *);
|
||||
};
|
||||
|
||||
struct iio_dev *ad7606_probe(struct device *dev, int irq,
|
||||
void __iomem *base_address, unsigned int id,
|
||||
const struct ad7606_bus_ops *bops);
|
||||
int ad7606_remove(struct iio_dev *indio_dev, int irq);
|
||||
int ad7606_reset(struct ad7606_state *st);
|
||||
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
const char *name, unsigned int id,
|
||||
const struct ad7606_bus_ops *bops);
|
||||
int ad7606_remove(struct device *dev, int irq);
|
||||
|
||||
enum ad7606_supported_device_ids {
|
||||
ID_AD7606_8,
|
||||
@ -97,9 +68,6 @@ enum ad7606_supported_device_ids {
|
||||
ID_AD7606_4
|
||||
};
|
||||
|
||||
int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev);
|
||||
void ad7606_ring_cleanup(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern const struct dev_pm_ops ad7606_pm_ops;
|
||||
#define AD7606_PM_OPS (&ad7606_pm_ops)
|
||||
|
@ -49,8 +49,8 @@ static const struct ad7606_bus_ops ad7606_par8_bops = {
|
||||
|
||||
static int ad7606_par_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev;
|
||||
void __iomem *addr;
|
||||
resource_size_t remap_size;
|
||||
int irq;
|
||||
@ -68,26 +68,15 @@ static int ad7606_par_probe(struct platform_device *pdev)
|
||||
|
||||
remap_size = resource_size(res);
|
||||
|
||||
indio_dev = ad7606_probe(&pdev->dev, irq, addr,
|
||||
platform_get_device_id(pdev)->driver_data,
|
||||
remap_size > 1 ? &ad7606_par16_bops :
|
||||
&ad7606_par8_bops);
|
||||
|
||||
if (IS_ERR(indio_dev))
|
||||
return PTR_ERR(indio_dev);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
return ad7606_probe(&pdev->dev, irq, addr,
|
||||
id->name, id->driver_data,
|
||||
remap_size > 1 ? &ad7606_par16_bops :
|
||||
&ad7606_par8_bops);
|
||||
}
|
||||
|
||||
static int ad7606_par_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
ad7606_remove(indio_dev, platform_get_irq(pdev, 0));
|
||||
|
||||
return 0;
|
||||
return ad7606_remove(&pdev->dev, platform_get_irq(pdev, 0));
|
||||
}
|
||||
|
||||
static const struct platform_device_id ad7606_driver_ids[] = {
|
||||
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include "ad7606.h"
|
||||
|
||||
/**
|
||||
* ad7606_trigger_handler_th() th/bh of trigger launched polling to ring buffer
|
||||
*
|
||||
**/
|
||||
static irqreturn_t ad7606_trigger_handler_th_bh(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct ad7606_state *st = iio_priv(pf->indio_dev);
|
||||
|
||||
gpio_set_value(st->pdata->gpio_convst, 1);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer
|
||||
* @work_s: the work struct through which this was scheduled
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
* I think the one copy of this at a time was to avoid problems if the
|
||||
* trigger was set far too high and the reads then locked up the computer.
|
||||
**/
|
||||
static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
|
||||
{
|
||||
struct ad7606_state *st = container_of(work_s, struct ad7606_state,
|
||||
poll_work);
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(st);
|
||||
__u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(st->pdata->gpio_frstdata)) {
|
||||
ret = st->bops->read_block(st->dev, 1, buf);
|
||||
if (ret)
|
||||
goto done;
|
||||
if (!gpio_get_value(st->pdata->gpio_frstdata)) {
|
||||
/* This should never happen. However
|
||||
* some signal glitch caused by bad PCB desgin or
|
||||
* electrostatic discharge, could cause an extra read
|
||||
* or clock. This allows recovery.
|
||||
*/
|
||||
ad7606_reset(st);
|
||||
goto done;
|
||||
}
|
||||
ret = st->bops->read_block(st->dev,
|
||||
st->chip_info->num_channels - 1, buf + 2);
|
||||
if (ret)
|
||||
goto done;
|
||||
} else {
|
||||
ret = st->bops->read_block(st->dev,
|
||||
st->chip_info->num_channels, buf);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
done:
|
||||
gpio_set_value(st->pdata->gpio_convst, 0);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
|
||||
|
||||
return iio_triggered_buffer_setup(indio_dev,
|
||||
&ad7606_trigger_handler_th_bh, &ad7606_trigger_handler_th_bh,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void ad7606_ring_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
@ -42,25 +42,16 @@ static const struct ad7606_bus_ops ad7606_spi_bops = {
|
||||
|
||||
static int ad7606_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
indio_dev = ad7606_probe(&spi->dev, spi->irq, NULL,
|
||||
spi_get_device_id(spi)->driver_data,
|
||||
&ad7606_spi_bops);
|
||||
|
||||
if (IS_ERR(indio_dev))
|
||||
return PTR_ERR(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
return 0;
|
||||
return ad7606_probe(&spi->dev, spi->irq, NULL,
|
||||
id->name, id->driver_data,
|
||||
&ad7606_spi_bops);
|
||||
}
|
||||
|
||||
static int ad7606_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev);
|
||||
|
||||
return ad7606_remove(indio_dev, spi->irq);
|
||||
return ad7606_remove(&spi->dev, spi->irq);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7606_id[] = {
|
||||
|
@ -327,7 +327,7 @@ static struct attribute *ad7816_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad7816_event_attribute_group = {
|
||||
static const struct attribute_group ad7816_event_attribute_group = {
|
||||
.attrs = ad7816_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
@ -2039,7 +2039,7 @@ static struct attribute *adt7316_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group adt7316_event_attribute_group = {
|
||||
static const struct attribute_group adt7316_event_attribute_group = {
|
||||
.attrs = adt7316_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
@ -2060,7 +2060,7 @@ static struct attribute *adt7516_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group adt7516_event_attribute_group = {
|
||||
static const struct attribute_group adt7516_event_attribute_group = {
|
||||
.attrs = adt7516_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
@ -562,7 +562,7 @@ static struct attribute *ad7150_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad7150_event_attribute_group = {
|
||||
static const struct attribute_group ad7150_event_attribute_group = {
|
||||
.attrs = ad7150_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
@ -89,6 +89,7 @@ struct ad7152_chip_info {
|
||||
*/
|
||||
u8 filter_rate_setup;
|
||||
u8 setup[2];
|
||||
struct mutex state_lock; /* protect hardware state */
|
||||
};
|
||||
|
||||
static inline ssize_t ad7152_start_calib(struct device *dev,
|
||||
@ -115,10 +116,10 @@ static inline ssize_t ad7152_start_calib(struct device *dev,
|
||||
else
|
||||
regval |= AD7152_CONF_CH2EN;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&chip->state_lock);
|
||||
ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -126,14 +127,15 @@ static inline ssize_t ad7152_start_calib(struct device *dev,
|
||||
mdelay(20);
|
||||
ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
} while ((ret == regval) && timeout--);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t ad7152_start_offset_calib(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
@ -142,6 +144,7 @@ static ssize_t ad7152_start_offset_calib(struct device *dev,
|
||||
return ad7152_start_calib(dev, attr, buf, len,
|
||||
AD7152_CONF_MODE_OFFS_CAL);
|
||||
}
|
||||
|
||||
static ssize_t ad7152_start_gain_calib(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
@ -165,63 +168,12 @@ static const unsigned char ad7152_filter_rate_table[][2] = {
|
||||
{200, 5 + 1}, {50, 20 + 1}, {20, 50 + 1}, {17, 60 + 1},
|
||||
};
|
||||
|
||||
static ssize_t ad7152_show_filter_rate_setup(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
ad7152_filter_rate_table[chip->filter_rate_setup][0]);
|
||||
}
|
||||
|
||||
static ssize_t ad7152_store_filter_rate_setup(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
u8 data;
|
||||
int ret, i;
|
||||
|
||||
ret = kstrtou8(buf, 10, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++)
|
||||
if (data >= ad7152_filter_rate_table[i][0])
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(ad7152_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7152_filter_rate_table) - 1;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
AD7152_REG_CFG2, AD7152_CFG2_OSR(i));
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->filter_rate_setup = i;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
|
||||
ad7152_show_filter_rate_setup,
|
||||
ad7152_store_filter_rate_setup);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("200 50 20 17");
|
||||
|
||||
static IIO_CONST_ATTR(in_capacitance_scale_available,
|
||||
"0.000061050 0.000030525 0.000015263 0.000007631");
|
||||
|
||||
static struct attribute *ad7152_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
|
||||
@ -247,6 +199,51 @@ static const int ad7152_scale_table[] = {
|
||||
30525, 7631, 15263, 61050
|
||||
};
|
||||
|
||||
/**
|
||||
* read_raw handler for IIO_CHAN_INFO_SAMP_FREQ
|
||||
*
|
||||
* lock must be held
|
||||
**/
|
||||
static int ad7152_read_raw_samp_freq(struct device *dev, int *val)
|
||||
{
|
||||
struct ad7152_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
*val = ad7152_filter_rate_table[chip->filter_rate_setup][0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_raw handler for IIO_CHAN_INFO_SAMP_FREQ
|
||||
*
|
||||
* lock must be held
|
||||
**/
|
||||
static int ad7152_write_raw_samp_freq(struct device *dev, int val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++)
|
||||
if (val >= ad7152_filter_rate_table[i][0])
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(ad7152_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7152_filter_rate_table) - 1;
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
AD7152_REG_CFG2, AD7152_CFG2_OSR(i));
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->filter_rate_setup = i;
|
||||
mutex_unlock(&chip->state_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int ad7152_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
@ -256,7 +253,7 @@ static int ad7152_write_raw(struct iio_dev *indio_dev,
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&chip->state_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
@ -307,6 +304,17 @@ static int ad7152_write_raw(struct iio_dev *indio_dev,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = ad7152_write_raw_samp_freq(&indio_dev->dev, val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
@ -314,9 +322,10 @@ static int ad7152_write_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7152_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
@ -326,7 +335,7 @@ static int ad7152_read_raw(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
u8 regval = 0;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&chip->state_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@ -403,11 +412,18 @@ static int ad7152_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ad7152_read_raw_samp_freq(&indio_dev->dev, val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -440,6 +456,7 @@ static const struct iio_chan_spec ad7152_channels[] = {
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}, {
|
||||
.type = IIO_CAPACITANCE,
|
||||
.differential = 1,
|
||||
@ -450,6 +467,7 @@ static const struct iio_chan_spec ad7152_channels[] = {
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}, {
|
||||
.type = IIO_CAPACITANCE,
|
||||
.indexed = 1,
|
||||
@ -458,6 +476,7 @@ static const struct iio_chan_spec ad7152_channels[] = {
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}, {
|
||||
.type = IIO_CAPACITANCE,
|
||||
.differential = 1,
|
||||
@ -468,8 +487,10 @@ static const struct iio_chan_spec ad7152_channels[] = {
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* device probe and remove
|
||||
*/
|
||||
@ -489,6 +510,7 @@ static int ad7152_probe(struct i2c_client *client,
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
chip->client = client;
|
||||
mutex_init(&chip->state_lock);
|
||||
|
||||
/* Establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->name = id->name;
|
||||
|
@ -122,7 +122,8 @@ static const struct iio_chan_spec ad7746_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7746_REG_VT_DATA_HIGH << 8 |
|
||||
AD7746_VTSETUP_VTMD_EXT_VIN,
|
||||
},
|
||||
@ -132,7 +133,8 @@ static const struct iio_chan_spec ad7746_channels[] = {
|
||||
.channel = 1,
|
||||
.extend_name = "supply",
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7746_REG_VT_DATA_HIGH << 8 |
|
||||
AD7746_VTSETUP_VTMD_VDD_MON,
|
||||
},
|
||||
@ -159,7 +161,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7746_REG_CAP_DATA_HIGH << 8,
|
||||
},
|
||||
[CIN1_DIFF] = {
|
||||
@ -171,7 +173,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7746_REG_CAP_DATA_HIGH << 8 |
|
||||
AD7746_CAPSETUP_CAPDIFF
|
||||
},
|
||||
@ -182,7 +184,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7746_REG_CAP_DATA_HIGH << 8 |
|
||||
AD7746_CAPSETUP_CIN2,
|
||||
},
|
||||
@ -195,7 +197,7 @@ static const struct iio_chan_spec ad7746_channels[] = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7746_REG_CAP_DATA_HIGH << 8 |
|
||||
AD7746_CAPSETUP_CAPDIFF | AD7746_CAPSETUP_CIN2,
|
||||
}
|
||||
@ -355,101 +357,47 @@ static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
|
||||
static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration,
|
||||
S_IWUSR, NULL, ad7746_start_gain_calib, VIN);
|
||||
|
||||
static ssize_t ad7746_show_cap_filter_rate_setup(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
|
||||
int val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7746_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ad7746_cap_filter_rate_table[
|
||||
(chip->config >> 3) & 0x7][0]);
|
||||
}
|
||||
|
||||
static ssize_t ad7746_store_cap_filter_rate_setup(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7746_chip_info *chip = iio_priv(indio_dev);
|
||||
u8 data;
|
||||
int ret, i;
|
||||
|
||||
ret = kstrtou8(buf, 10, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++)
|
||||
if (data >= ad7746_cap_filter_rate_table[i][0])
|
||||
if (val >= ad7746_cap_filter_rate_table[i][0])
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
chip->config &= ~AD7746_CONF_CAPFS(0x7);
|
||||
chip->config |= AD7746_CONF_CAPFS(i);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ad7746_show_vt_filter_rate_setup(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
|
||||
int val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7746_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ad7746_vt_filter_rate_table[
|
||||
(chip->config >> 6) & 0x3][0]);
|
||||
}
|
||||
|
||||
static ssize_t ad7746_store_vt_filter_rate_setup(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7746_chip_info *chip = iio_priv(indio_dev);
|
||||
u8 data;
|
||||
int ret, i;
|
||||
|
||||
ret = kstrtou8(buf, 10, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++)
|
||||
if (data >= ad7746_vt_filter_rate_table[i][0])
|
||||
if (val >= ad7746_vt_filter_rate_table[i][0])
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
chip->config &= ~AD7746_CONF_VTFS(0x3);
|
||||
chip->config |= AD7746_CONF_VTFS(i);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_capacitance_sampling_frequency,
|
||||
S_IRUGO | S_IWUSR, ad7746_show_cap_filter_rate_setup,
|
||||
ad7746_store_cap_filter_rate_setup, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage_sampling_frequency,
|
||||
S_IRUGO | S_IWUSR, ad7746_show_vt_filter_rate_setup,
|
||||
ad7746_store_vt_filter_rate_setup, 0);
|
||||
|
||||
static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8");
|
||||
static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available,
|
||||
"91 84 50 26 16 13 11 9");
|
||||
|
||||
static struct attribute *ad7746_attributes[] = {
|
||||
&iio_dev_attr_in_capacitance_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
|
||||
@ -547,6 +495,23 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CAPACITANCE:
|
||||
ret = ad7746_store_cap_filter_rate_setup(chip, val);
|
||||
break;
|
||||
case IIO_VOLTAGE:
|
||||
ret = ad7746_store_vt_filter_rate_setup(chip, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
@ -666,6 +631,21 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (chan->type) {
|
||||
case IIO_CAPACITANCE:
|
||||
*val = ad7746_cap_filter_rate_table[
|
||||
(chip->config >> 3) & 0x7][0];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_VOLTAGE:
|
||||
*val = ad7746_vt_filter_rate_table[
|
||||
(chip->config >> 6) & 0x3][0];
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -3,18 +3,6 @@
|
||||
#
|
||||
menu "Light sensors"
|
||||
|
||||
config SENSORS_ISL29018
|
||||
tristate "ISL 29018 light and proximity sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for ambient light sensing and
|
||||
proximity infrared sensing from Intersil ISL29018.
|
||||
This driver will provide the measurements of ambient light intensity
|
||||
in lux, proximity infrared sensing and normal infrared sensing.
|
||||
Data from sensor is accessible via sysfs.
|
||||
|
||||
config SENSORS_ISL29028
|
||||
tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor"
|
||||
depends on I2C
|
||||
|
@ -2,7 +2,6 @@
|
||||
# Makefile for industrial I/O Light sensors
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
|
||||
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
|
||||
obj-$(CONFIG_TSL2583) += tsl2583.o
|
||||
obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o
|
||||
|
@ -465,38 +465,26 @@ err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ade7758_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static int ade7758_read_samp_freq(struct device *dev, int *val)
|
||||
{
|
||||
int ret;
|
||||
u8 t;
|
||||
int sps;
|
||||
|
||||
ret = ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
t = (t >> 5) & 0x3;
|
||||
sps = 26040 / (1 << t);
|
||||
*val = 26040 / (1 << t);
|
||||
|
||||
return sprintf(buf, "%d SPS\n", sps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ade7758_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
static int ade7758_write_samp_freq(struct device *dev, int val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
u16 val;
|
||||
int ret;
|
||||
u8 reg, t;
|
||||
|
||||
ret = kstrtou16(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
switch (val) {
|
||||
case 26040:
|
||||
t = 0;
|
||||
@ -525,9 +513,49 @@ static ssize_t ade7758_write_frequency(struct device *dev,
|
||||
ret = ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
|
||||
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret ? ret : len;
|
||||
static int ade7758_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ade7758_read_samp_freq(&indio_dev->dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ade7758_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ade7758_write_samp_freq(&indio_dev->dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_TEMP_RAW(ade7758_read_8bit);
|
||||
@ -553,17 +581,12 @@ static IIO_DEV_ATTR_BVAHR(ade7758_read_16bit,
|
||||
static IIO_DEV_ATTR_CVAHR(ade7758_read_16bit,
|
||||
ADE7758_CVAHR);
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ade7758_read_frequency,
|
||||
ade7758_write_frequency);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26040 13020 6510 3255");
|
||||
|
||||
static struct attribute *ade7758_attributes[] = {
|
||||
&iio_dev_attr_in_temp_raw.dev_attr.attr,
|
||||
&iio_const_attr_in_temp_offset.dev_attr.attr,
|
||||
&iio_const_attr_in_temp_scale.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_awatthr.dev_attr.attr,
|
||||
&iio_dev_attr_bwatthr.dev_attr.attr,
|
||||
@ -611,6 +634,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_A, AD7758_VOLTAGE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
@ -622,6 +646,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.type = IIO_CURRENT,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_A, AD7758_CURRENT),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
@ -634,6 +659,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "apparent",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_A, AD7758_APP_PWR),
|
||||
.scan_index = 2,
|
||||
.scan_type = {
|
||||
@ -646,6 +672,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "active",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_A, AD7758_ACT_PWR),
|
||||
.scan_index = 3,
|
||||
.scan_type = {
|
||||
@ -658,6 +685,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "reactive",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_A, AD7758_REACT_PWR),
|
||||
.scan_index = 4,
|
||||
.scan_type = {
|
||||
@ -669,6 +697,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_B, AD7758_VOLTAGE),
|
||||
.scan_index = 5,
|
||||
.scan_type = {
|
||||
@ -680,6 +709,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.type = IIO_CURRENT,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_B, AD7758_CURRENT),
|
||||
.scan_index = 6,
|
||||
.scan_type = {
|
||||
@ -692,6 +722,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.extend_name = "apparent",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_B, AD7758_APP_PWR),
|
||||
.scan_index = 7,
|
||||
.scan_type = {
|
||||
@ -704,6 +735,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.extend_name = "active",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_B, AD7758_ACT_PWR),
|
||||
.scan_index = 8,
|
||||
.scan_type = {
|
||||
@ -716,6 +748,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.extend_name = "reactive",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_B, AD7758_REACT_PWR),
|
||||
.scan_index = 9,
|
||||
.scan_type = {
|
||||
@ -727,6 +760,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 2,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_C, AD7758_VOLTAGE),
|
||||
.scan_index = 10,
|
||||
.scan_type = {
|
||||
@ -738,6 +772,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.type = IIO_CURRENT,
|
||||
.indexed = 1,
|
||||
.channel = 2,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_C, AD7758_CURRENT),
|
||||
.scan_index = 11,
|
||||
.scan_type = {
|
||||
@ -750,6 +785,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 2,
|
||||
.extend_name = "apparent",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_C, AD7758_APP_PWR),
|
||||
.scan_index = 12,
|
||||
.scan_type = {
|
||||
@ -762,6 +798,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 2,
|
||||
.extend_name = "active",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_C, AD7758_ACT_PWR),
|
||||
.scan_index = 13,
|
||||
.scan_type = {
|
||||
@ -774,6 +811,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 2,
|
||||
.extend_name = "reactive",
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = AD7758_WT(AD7758_PHASE_C, AD7758_REACT_PWR),
|
||||
.scan_index = 14,
|
||||
.scan_type = {
|
||||
@ -787,6 +825,8 @@ static const struct iio_chan_spec ade7758_channels[] = {
|
||||
|
||||
static const struct iio_info ade7758_info = {
|
||||
.attrs = &ade7758_attribute_group,
|
||||
.read_raw = &ade7758_read_raw,
|
||||
.write_raw = &ade7758_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* ring_hw.h - common functionality for iio hardware ring buffers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RING_HW_H_
|
||||
#define _RING_HW_H_
|
||||
|
||||
/**
|
||||
* struct iio_hw_ring_buffer- hardware ring buffer
|
||||
* @buf: generic ring buffer elements
|
||||
* @private: device specific data
|
||||
*/
|
||||
struct iio_hw_buffer {
|
||||
struct iio_buffer buf;
|
||||
void *private;
|
||||
};
|
||||
|
||||
#define iio_to_hw_buf(r) container_of(r, struct iio_hw_buffer, buf)
|
||||
|
||||
#endif /* _RING_HW_H_ */
|
@ -136,6 +136,7 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig);
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
|
@ -235,6 +235,19 @@ int iio_write_channel_raw(struct iio_channel *chan, int val);
|
||||
int iio_get_channel_type(struct iio_channel *channel,
|
||||
enum iio_chan_type *type);
|
||||
|
||||
/**
|
||||
* iio_read_channel_offset() - read the offset value for a channel
|
||||
* @chan: The channel being queried.
|
||||
* @val: First part of value read back.
|
||||
* @val2: Second part of value read back.
|
||||
*
|
||||
* Note returns a description of what is in val and val2, such
|
||||
* as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val
|
||||
* + val2/1e6
|
||||
*/
|
||||
int iio_read_channel_offset(struct iio_channel *chan, int *val,
|
||||
int *val2);
|
||||
|
||||
/**
|
||||
* iio_read_channel_scale() - read the scale value for a channel
|
||||
* @chan: The channel being queried.
|
||||
|
@ -9,8 +9,18 @@
|
||||
#ifndef IIO_DAC_MCP4725_H_
|
||||
#define IIO_DAC_MCP4725_H_
|
||||
|
||||
/**
|
||||
* struct mcp4725_platform_data - MCP4725/6 DAC specific data.
|
||||
* @use_vref: Whether an external reference voltage on Vref pin should be used.
|
||||
* Additional vref-supply must be specified when used.
|
||||
* @vref_buffered: Controls buffering of the external reference voltage.
|
||||
*
|
||||
* Vref related settings are available only on MCP4756. See
|
||||
* Documentation/devicetree/bindings/iio/dac/mcp4725.txt for more information.
|
||||
*/
|
||||
struct mcp4725_platform_data {
|
||||
u16 vref_mv;
|
||||
bool use_vref;
|
||||
bool vref_buffered;
|
||||
};
|
||||
|
||||
#endif /* IIO_DAC_MCP4725_H_ */
|
||||
|
@ -381,7 +381,7 @@ struct iio_dev;
|
||||
**/
|
||||
struct iio_info {
|
||||
struct module *driver_module;
|
||||
struct attribute_group *event_attrs;
|
||||
const struct attribute_group *event_attrs;
|
||||
const struct attribute_group *attrs;
|
||||
|
||||
int (*read_raw)(struct iio_dev *indio_dev,
|
||||
|
@ -55,10 +55,34 @@ struct iio_const_attr {
|
||||
{ .dev_attr = __ATTR(_name, _mode, _show, _store), \
|
||||
.address = _addr }
|
||||
|
||||
#define IIO_ATTR_RO(_name, _addr) \
|
||||
{ .dev_attr = __ATTR_RO(_name), \
|
||||
.address = _addr }
|
||||
|
||||
#define IIO_ATTR_WO(_name, _addr) \
|
||||
{ .dev_attr = __ATTR_WO(_name), \
|
||||
.address = _addr }
|
||||
|
||||
#define IIO_ATTR_RW(_name, _addr) \
|
||||
{ .dev_attr = __ATTR_RW(_name), \
|
||||
.address = _addr }
|
||||
|
||||
#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \
|
||||
struct iio_dev_attr iio_dev_attr_##_name \
|
||||
= IIO_ATTR(_name, _mode, _show, _store, _addr)
|
||||
|
||||
#define IIO_DEVICE_ATTR_RO(_name, _addr) \
|
||||
struct iio_dev_attr iio_dev_attr_##_name \
|
||||
= IIO_ATTR_RO(_name, _addr)
|
||||
|
||||
#define IIO_DEVICE_ATTR_WO(_name, _addr) \
|
||||
struct iio_dev_attr iio_dev_attr_##_name \
|
||||
= IIO_ATTR_WO(_name, _addr)
|
||||
|
||||
#define IIO_DEVICE_ATTR_RW(_name, _addr) \
|
||||
struct iio_dev_attr iio_dev_attr_##_name \
|
||||
= IIO_ATTR_RW(_name, _addr)
|
||||
|
||||
#define IIO_DEVICE_ATTR_NAMED(_vname, _name, _mode, _show, _store, _addr) \
|
||||
struct iio_dev_attr iio_dev_attr_##_vname \
|
||||
= IIO_ATTR(_name, _mode, _show, _store, _addr)
|
||||
|
@ -170,6 +170,8 @@ void iio_trigger_free(struct iio_trigger *trig);
|
||||
*/
|
||||
bool iio_trigger_using_own(struct iio_dev *indio_dev);
|
||||
|
||||
int iio_trigger_validate_own_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev);
|
||||
|
||||
#else
|
||||
struct iio_trigger;
|
||||
|
@ -40,6 +40,8 @@ enum iio_chan_type {
|
||||
IIO_PH,
|
||||
IIO_UVINDEX,
|
||||
IIO_ELECTRICALCONDUCTIVITY,
|
||||
IIO_COUNT,
|
||||
IIO_INDEX,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
|
@ -247,6 +247,7 @@ void print_usage(void)
|
||||
fprintf(stderr, "Usage: generic_buffer [options]...\n"
|
||||
"Capture, convert and output data from IIO device buffer\n"
|
||||
" -a Auto-activate all available channels\n"
|
||||
" -A Force-activate ALL channels\n"
|
||||
" -c <n> Do n conversions\n"
|
||||
" -e Disable wait for event (new data)\n"
|
||||
" -g Use trigger-less mode\n"
|
||||
@ -347,16 +348,22 @@ int main(int argc, char **argv)
|
||||
int noevents = 0;
|
||||
int notrigger = 0;
|
||||
char *dummy;
|
||||
bool force_autochannels = false;
|
||||
|
||||
struct iio_channel_info *channels = NULL;
|
||||
|
||||
register_cleanup();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "ac:egl:n:N:t:T:w:", longopts, NULL)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "aAc:egl:n:N:t:T:w:?", longopts,
|
||||
NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
autochannels = AUTOCHANNELS_ENABLED;
|
||||
break;
|
||||
case 'A':
|
||||
autochannels = AUTOCHANNELS_ENABLED;
|
||||
force_autochannels = true;
|
||||
break;
|
||||
case 'c':
|
||||
errno = 0;
|
||||
num_loops = strtoul(optarg, &dummy, 10);
|
||||
@ -519,15 +526,16 @@ int main(int argc, char **argv)
|
||||
"diag %s\n", dev_dir_name);
|
||||
goto error;
|
||||
}
|
||||
if (num_channels && autochannels == AUTOCHANNELS_ENABLED) {
|
||||
if (num_channels && autochannels == AUTOCHANNELS_ENABLED &&
|
||||
!force_autochannels) {
|
||||
fprintf(stderr, "Auto-channels selected but some channels "
|
||||
"are already activated in sysfs\n");
|
||||
fprintf(stderr, "Proceeding without activating any channels\n");
|
||||
}
|
||||
|
||||
if (!num_channels && autochannels == AUTOCHANNELS_ENABLED) {
|
||||
fprintf(stderr,
|
||||
"No channels are enabled, enabling all channels\n");
|
||||
if ((!num_channels && autochannels == AUTOCHANNELS_ENABLED) ||
|
||||
(autochannels == AUTOCHANNELS_ENABLED && force_autochannels)) {
|
||||
fprintf(stderr, "Enabling all channels\n");
|
||||
|
||||
ret = enable_disable_all_channels(dev_dir_name, 1);
|
||||
if (ret) {
|
||||
|
Loading…
Reference in New Issue
Block a user