Staging / IIO driver patches for 5.2-rc1
Here is the big staging and iio driver update for 5.2-rc1. Lots of tiny fixes all over the staging and IIO driver trees here, along with some new IIO drivers. Also we ended up deleting two drivers, making this pull request remove a few hundred thousand lines of code, always a nice thing to see. Both of the drivers removed have been replaced with "real" drivers in their various subsystem directories, and they will be coming to you from those locations during this merge window. There are some core vt/selection changes in here, that was due to some cleanups needed for the speakup fixes. Those have all been acked by the various subsystem maintainers (i.e. me), so those are ok. We also added a few new drivers, for some odd hardware, giving new developers plenty to work on with basic coding style cleanups to come in the near future. Other than that, nothing unusual here. All of these have been in linux-next for a while with no reported issues, other than an odd gcc warning for one of the new drivers that should be fixed up soon. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXNHGMQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ynQRACgwtlC6DFsEFwCplYxQXP5uzuIVTMAoJ61xzC0 Qim7K31f5ulaa3GJuhzo =zEY5 -----END PGP SIGNATURE----- Merge tag 'staging-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging / IIO driver updates from Greg KH: "Here is the big staging and iio driver update for 5.2-rc1. Lots of tiny fixes all over the staging and IIO driver trees here, along with some new IIO drivers. The "counter" subsystem was added in here as well, as it is needed by the IIO drivers and subsystem. Also we ended up deleting two drivers, making this pull request remove a few hundred thousand lines of code, always a nice thing to see. Both of the drivers removed have been replaced with "real" drivers in their various subsystem directories, and they will be coming to you from those locations during this merge window. There are some core vt/selection changes in here, that was due to some cleanups needed for the speakup fixes. Those have all been acked by the various subsystem maintainers (i.e. me), so those are ok. We also added a few new drivers, for some odd hardware, giving new developers plenty to work on with basic coding style cleanups to come in the near future. Other than that, nothing unusual here. All of these have been in linux-next for a while with no reported issues, other than an odd gcc warning for one of the new drivers that should be fixed up soon" [ I fixed up the warning myself - Linus ] * tag 'staging-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (663 commits) staging: kpc2000: kpc_spi: Fix build error for {read,write}q Staging: rtl8192e: Remove extra space before break statement Staging: rtl8192u: ieee80211: Fix if-else indentation warning Staging: rtl8192u: ieee80211: Fix indentation errors by removing extra spaces staging: most: cdev: fix chrdev_region leak in mod_exit staging: wlan-ng: Fix improper SPDX comment style staging: rtl8192u: ieee80211: Resolve ERROR reported by checkpatch staging: vc04_services: bcm2835-camera: Compress two lines into one line staging: rtl8723bs: core: Use !x in place of NULL comparison. staging: rtl8723bs: core: Prefer using the BIT Macro. staging: fieldbus: anybus-s: fix wait_for_completion_timeout return handling staging: kpc2000: fix up build problems with readq() staging: rtlwifi: move remaining phydm .h files staging: rtlwifi: strip down phydm .h files staging: rtlwifi: delete the staging driver staging: fieldbus: anybus-s: rename bus id field to avoid confusion staging: fieldbus: anybus-s: keep device bus id in bus endianness Staging: sm750fb: Change *array into *const array staging: rtl8192u: ieee80211: Fix spelling mistake staging: rtl8192u: ieee80211: Replace bit shifting with BIT macro ...
This commit is contained in:
Коммит
e0dccbdf5a
1
.mailmap
1
.mailmap
|
@ -189,6 +189,7 @@ Santosh Shilimkar <ssantosh@kernel.org>
|
|||
Santosh Shilimkar <santosh.shilimkar@oracle.org>
|
||||
Sascha Hauer <s.hauer@pengutronix.de>
|
||||
S.Çağlar Onur <caglar@pardus.org.tr>
|
||||
Sean Nyekjaer <sean@geanix.com> <sean.nyekjaer@prevas.dk>
|
||||
Sebastian Reichel <sre@kernel.org> <sre@debian.org>
|
||||
Sebastian Reichel <sre@kernel.org> <sebastian.reichel@collabora.co.uk>
|
||||
Shiraz Hashim <shiraz.linux.kernel@gmail.com> <shiraz.hashim@st.com>
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
What: /sys/bus/counter/devices/counterX/countY/count
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count data of Count Y represented as a string.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/ceiling
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count value ceiling for Count Y. This is the upper limit for the
|
||||
respective counter.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/floor
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count value floor for Count Y. This is the lower limit for the
|
||||
respective counter.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/count_mode
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count mode for channel Y. The ceiling and floor values for
|
||||
Count Y are used by the count mode where required. The following
|
||||
count modes are available:
|
||||
|
||||
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 Count Y ceiling value, while the lower limit is set
|
||||
to the Count Y floor value. The counter freezes at
|
||||
count = ceiling when counting up, and at count = floor
|
||||
when counting down. At either of these limits, the
|
||||
counting is resumed only when the count direction is
|
||||
reversed.
|
||||
|
||||
non-recycle:
|
||||
The counter is disabled whenever a counter 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 direct write.
|
||||
|
||||
modulo-n:
|
||||
A count value boundary is set between the Count Y floor
|
||||
value and the Count Y ceiling value. The counter is
|
||||
reset to the Count Y floor value at count = ceiling when
|
||||
counting up, while the counter is set to the Count Y
|
||||
ceiling value at count = floor when counting down; the
|
||||
counter does not freeze at the boundary points, but
|
||||
counts continuously throughout.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/count_mode_available
|
||||
What: /sys/bus/counter/devices/counterX/countY/error_noise_available
|
||||
What: /sys/bus/counter/devices/counterX/countY/function_available
|
||||
What: /sys/bus/counter/devices/counterX/countY/signalZ_action_available
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Discrete set of available values for the respective Count Y
|
||||
configuration are listed in this file. Values are delimited by
|
||||
newline characters.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/direction
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the count direction of Count
|
||||
Y. Two count directions are available: forward and backward.
|
||||
|
||||
Some counter devices are able to determine the direction of
|
||||
their counting. For example, quadrature encoding counters can
|
||||
determine the direction of movement by evaluating the leading
|
||||
phase of the respective A and B quadrature encoding signals.
|
||||
This attribute exposes such count directions.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/enable
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Whether channel Y counter is enabled. Valid attribute values are
|
||||
boolean.
|
||||
|
||||
This attribute is intended to serve as a pause/unpause mechanism
|
||||
for Count Y. Suppose a counter device is used to count the total
|
||||
movement of a conveyor belt: this attribute allows an operator
|
||||
to temporarily pause the counter, service the conveyor belt,
|
||||
and then finally unpause the counter to continue where it had
|
||||
left off.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/error_noise
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates whether excessive noise is
|
||||
present at the channel Y counter inputs.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/function
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count function mode of Count Y; count function evaluation is
|
||||
triggered by conditions specified by the Count Y signalZ_action
|
||||
attributes. The following count functions are available:
|
||||
|
||||
increase:
|
||||
Accumulated count is incremented.
|
||||
|
||||
decrease:
|
||||
Accumulated count is decremented.
|
||||
|
||||
pulse-direction:
|
||||
Rising edges on signal A updates the respective count.
|
||||
The input level of signal B determines direction.
|
||||
|
||||
quadrature x1 a:
|
||||
If direction is forward, rising edges on quadrature pair
|
||||
signal A updates the respective count; if the direction
|
||||
is backward, falling edges on quadrature pair signal A
|
||||
updates the respective count. Quadrature encoding
|
||||
determines the direction.
|
||||
|
||||
quadrature x1 b:
|
||||
If direction is forward, rising edges on quadrature pair
|
||||
signal B updates the respective count; if the direction
|
||||
is backward, falling edges on quadrature pair signal B
|
||||
updates the respective count. Quadrature encoding
|
||||
determines the direction.
|
||||
|
||||
quadrature x2 a:
|
||||
Any state transition on quadrature pair signal A updates
|
||||
the respective count. Quadrature encoding determines the
|
||||
direction.
|
||||
|
||||
quadrature x2 b:
|
||||
Any state transition on quadrature pair signal B updates
|
||||
the respective count. Quadrature encoding determines the
|
||||
direction.
|
||||
|
||||
quadrature x4:
|
||||
Any state transition on either quadrature pair signals
|
||||
updates the respective count. Quadrature encoding
|
||||
determines the direction.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/name
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the device-specific name of
|
||||
Count Y. If possible, this should match the name of the
|
||||
respective channel as it appears in the device datasheet.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/preset
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If the counter device supports preset registers -- registers
|
||||
used to load counter channels to a set count upon device-defined
|
||||
preset operation trigger events -- the preset count for channel
|
||||
Y is provided by this attribute.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/preset_enable
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Whether channel Y counter preset operation is enabled. Valid
|
||||
attribute values are boolean.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/signalZ_action
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Action mode of Count Y for Signal Z. This attribute indicates
|
||||
the condition of Signal Z that triggers the count function
|
||||
evaluation for Count Y. The following action modes are
|
||||
available:
|
||||
|
||||
none:
|
||||
Signal does not trigger the count function. In
|
||||
Pulse-Direction count function mode, this Signal is
|
||||
evaluated as Direction.
|
||||
|
||||
rising edge:
|
||||
Low state transitions to high state.
|
||||
|
||||
falling edge:
|
||||
High state transitions to low state.
|
||||
|
||||
both edges:
|
||||
Any state transition.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/name
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the device-specific name of
|
||||
the Counter. This should match the name of the device as it
|
||||
appears in its respective datasheet.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/num_counts
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the total number of Counts
|
||||
belonging to the Counter.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/num_signals
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the total number of Signals
|
||||
belonging to the Counter.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/signal
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Signal data of Signal Y represented as a string.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/name
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the device-specific name of
|
||||
Signal Y. If possible, this should match the name of the
|
||||
respective signal as it appears in the device datasheet.
|
|
@ -0,0 +1,36 @@
|
|||
What: /sys/bus/counter/devices/counterX/signalY/index_polarity
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Active level of index input Signal Y; irrelevant in
|
||||
non-synchronous load mode.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/index_polarity_available
|
||||
What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_available
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Discrete set of available values for the respective Signal Y
|
||||
configuration are listed in this file.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Configure the counter associated with Signal Y for
|
||||
non-synchronous or synchronous load mode. Synchronous load mode
|
||||
cannot be selected in non-quadrature (Pulse-Direction) clock
|
||||
mode.
|
||||
|
||||
non-synchronous:
|
||||
A logic low level is the active level at this index
|
||||
input. The index function (as enabled via preset_enable)
|
||||
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
|
||||
preset_enable) is performed synchronously with the
|
||||
quadrature clock on the active level of the index input.
|
|
@ -0,0 +1,16 @@
|
|||
What: /sys/bus/counter/devices/counterX/countY/prescaler_available
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Discrete set of available values for the respective Count Y
|
||||
configuration are listed in this file. Values are delimited by
|
||||
newline characters.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/prescaler
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Configure the prescaler value associated with Count Y.
|
||||
On the FlexTimer, the counter clock source passes through a
|
||||
prescaler (i.e. a counter). This acts like a clock
|
||||
divider.
|
|
@ -1656,6 +1656,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
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.
|
||||
|
@ -1664,6 +1666,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_indexY_raw
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
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
|
||||
|
@ -1673,6 +1677,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
|
|||
KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
A list of possible counting directions which are:
|
||||
- "up" : counter device is increasing.
|
||||
- "down": counter device is decreasing.
|
||||
|
@ -1681,6 +1687,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
|
|||
KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
Raw counter device counters direction for channel Y.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw
|
||||
|
|
|
@ -6,6 +6,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
Discrete set of available values for the respective counter
|
||||
configuration are listed in this file.
|
||||
|
||||
|
@ -13,6 +15,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
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.
|
||||
|
@ -47,6 +51,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
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.
|
||||
|
@ -55,6 +61,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
If the counter device supports preset registers, the preset
|
||||
count for channel Y is provided by this attribute.
|
||||
|
||||
|
@ -62,6 +70,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
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
|
||||
|
@ -83,6 +93,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
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.
|
||||
|
@ -91,6 +103,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
Active level of channel Y index input; irrelevant in
|
||||
non-synchronous load mode.
|
||||
|
||||
|
@ -98,6 +112,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode
|
|||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This interface is deprecated; please use the Counter subsystem.
|
||||
|
||||
Configure channel Y counter for non-synchronous or synchronous
|
||||
load mode. Synchronous load mode cannot be selected in
|
||||
non-quadrature clock mode.
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/outY_freq_start
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_start
|
||||
Date: March 2019
|
||||
KernelVersion: 3.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Frequency sweep start frequency in Hz.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/outY_freq_increment
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_increment
|
||||
Date: March 2019
|
||||
KernelVersion: 3.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Frequency increment in Hz (step size) between consecutive
|
||||
frequency points along the sweep.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/outY_freq_points
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_points
|
||||
Date: March 2019
|
||||
KernelVersion: 3.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Number of frequency points (steps) in the frequency sweep.
|
||||
This value, in conjunction with the outY_freq_start and the
|
||||
outY_freq_increment, determines the frequency sweep range
|
||||
for the sweep operation.
|
||||
This value, in conjunction with the
|
||||
out_altvoltageY_frequency_start and the
|
||||
out_altvoltageY_frequency_increment, determines the frequency
|
||||
sweep range for the sweep operation.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/outY_settling_cycles
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_settling_cycles
|
||||
Date: March 2019
|
||||
KernelVersion: 3.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
|
@ -1,6 +1,6 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/start_cleaning
|
||||
Date: December 2018
|
||||
KernelVersion: 4.22
|
||||
KernelVersion: 5.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Writing 1 starts sensor self cleaning. Internal fan accelerates
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/fault_oc
|
||||
KernelVersion: 5.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Open-circuit fault. The detection of open-circuit faults,
|
||||
such as those caused by broken thermocouple wires.
|
||||
Reading returns either '1' or '0'.
|
||||
'1' = An open circuit such as broken thermocouple wires
|
||||
has been detected.
|
||||
'0' = No open circuit or broken thermocouple wires are detected
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/fault_ovuv
|
||||
KernelVersion: 5.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Overvoltage or Undervoltage Input Fault. The internal circuitry
|
||||
is protected from excessive voltages applied to the thermocouple
|
||||
cables by integrated MOSFETs at the T+ and T- inputs, and the
|
||||
BIAS output. These MOSFETs turn off when the input voltage is
|
||||
negative or greater than VDD.
|
||||
Reading returns either '1' or '0'.
|
||||
'1' = The input voltage is negative or greater than VDD.
|
||||
'0' = The input voltage is positive and less than VDD (normal
|
||||
state).
|
|
@ -0,0 +1,18 @@
|
|||
FlexTimer Quadrature decoder counter
|
||||
|
||||
This driver exposes a simple counter for the quadrature decoder mode.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "fsl,ftm-quaddec".
|
||||
- reg: Must be set to the memory region of the flextimer.
|
||||
|
||||
Optional property:
|
||||
- big-endian: Access the device registers in big-endian mode.
|
||||
|
||||
Example:
|
||||
counter0: counter@29d0000 {
|
||||
compatible = "fsl,ftm-quaddec";
|
||||
reg = <0x0 0x29d0000 0x0 0x10000>;
|
||||
big-endian;
|
||||
status = "disabled";
|
||||
};
|
|
@ -10,8 +10,9 @@ See ../mfd/stm32-lptimer.txt for details about the parent node.
|
|||
|
||||
Required properties:
|
||||
- compatible: Must be "st,stm32-lptimer-counter".
|
||||
- pinctrl-names: Set to "default".
|
||||
- pinctrl-0: List of phandles pointing to pin configuration nodes,
|
||||
- pinctrl-names: Set to "default". An additional "sleep" state can be
|
||||
defined to set pins in sleep state.
|
||||
- pinctrl-n: List of phandles pointing to pin configuration nodes,
|
||||
to set IN1/IN2 pins in mode of operation for Low-Power
|
||||
Timer input on external pin.
|
||||
|
||||
|
@ -21,7 +22,8 @@ Example:
|
|||
...
|
||||
counter {
|
||||
compatible = "st,stm32-lptimer-counter";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&lptim1_in_pins>;
|
||||
pinctrl-1 = <&lptim1_sleep_in_pins>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
STMicroelectronics STM32 Timer quadrature encoder
|
||||
|
||||
STM32 Timer provides quadrature encoder to detect
|
||||
angular position and direction of rotary elements,
|
||||
from IN1 and IN2 input signals.
|
||||
|
||||
Must be a sub-node of an STM32 Timer device tree node.
|
||||
See ../mfd/stm32-timers.txt for details about the parent node.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "st,stm32-timer-counter".
|
||||
- pinctrl-names: Set to "default".
|
||||
- pinctrl-0: List of phandles pointing to pin configuration nodes,
|
||||
to set CH1/CH2 pins in mode of operation for STM32
|
||||
Timer input on external pin.
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "int";
|
||||
|
||||
counter {
|
||||
compatible = "st,stm32-timer-counter";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&tim1_in_pins>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
* Arcx Anybus-S controller
|
||||
|
||||
This chip communicates with the SoC over a parallel bus. It is
|
||||
expected that its Device Tree node is specified as the child of a node
|
||||
corresponding to the parallel bus used for communication.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
|
||||
- compatible : The following chip-specific string:
|
||||
"arcx,anybus-controller"
|
||||
|
||||
- reg : three areas:
|
||||
index 0: bus memory area where the cpld registers are located.
|
||||
index 1: bus memory area of the first host's dual-port ram.
|
||||
index 2: bus memory area of the second host's dual-port ram.
|
||||
|
||||
- reset-gpios : the GPIO pin connected to the reset line of the controller.
|
||||
|
||||
- interrupts : two interrupts:
|
||||
index 0: interrupt connected to the first host
|
||||
index 1: interrupt connected to the second host
|
||||
Generic interrupt client node bindings are described in
|
||||
interrupt-controller/interrupts.txt
|
||||
|
||||
Optional: use of subnodes
|
||||
-------------------------
|
||||
|
||||
The card connected to a host may need additional properties. These can be
|
||||
specified in subnodes to the controller node.
|
||||
|
||||
The subnodes are identified by the standard 'reg' property. Which information
|
||||
exactly can be specified depends on the bindings for the function driver
|
||||
for the subnode.
|
||||
|
||||
Required controller node properties when using subnodes:
|
||||
- #address-cells: should be one.
|
||||
- #size-cells: should be zero.
|
||||
|
||||
Required subnode properties:
|
||||
- reg: Must contain the host index of the card this subnode describes:
|
||||
<0> for the first host on the controller
|
||||
<1> for the second host on the controller
|
||||
Note that only a single card can be plugged into a host, so the host
|
||||
index uniquely describes the card location.
|
||||
|
||||
Example of usage:
|
||||
-----------------
|
||||
|
||||
This example places the bridge on top of the i.MX WEIM parallel bus, see:
|
||||
Documentation/devicetree/bindings/bus/imx-weim.txt
|
||||
|
||||
&weim {
|
||||
controller@0,0 {
|
||||
compatible = "arcx,anybus-controller";
|
||||
reg = <0 0 0x100>, <0 0x400000 0x800>, <1 0x400000 0x800>;
|
||||
reset-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_LOW>, <5 IRQ_TYPE_LEVEL_LOW>;
|
||||
/* fsl,weim-cs-timing is a i.MX WEIM bus specific property */
|
||||
fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100
|
||||
0x00000000 0xa0000240 0x00000000>;
|
||||
/* optional subnode for a card plugged into the first host */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
card@0 {
|
||||
reg = <0>;
|
||||
/* card specific properties go here */
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
Kionix KXCJK-1013 Accelerometer device tree bindings
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be one of:
|
||||
"kionix,kxcjk1013"
|
||||
"kionix,kxcj91008"
|
||||
"kionix,kxtj21009"
|
||||
"kionix,kxtf9"
|
||||
- reg: i2c slave address
|
||||
|
||||
Example:
|
||||
|
||||
kxtf9@f {
|
||||
compatible = "kionix,kxtf9";
|
||||
reg = <0x0F>;
|
||||
};
|
|
@ -7,6 +7,7 @@ Required properties for the AD7606:
|
|||
* "adi,ad7606-8"
|
||||
* "adi,ad7606-6"
|
||||
* "adi,ad7606-4"
|
||||
* "adi,ad7616"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
* Analog Devices AD7170/AD7171/AD7780/AD7781
|
||||
|
||||
Data sheets:
|
||||
|
||||
- AD7170:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/AD7170.pdf
|
||||
- AD7171:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/AD7171.pdf
|
||||
- AD7780:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad7780.pdf
|
||||
- AD7781:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/AD7781.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be one of
|
||||
* "adi,ad7170"
|
||||
* "adi,ad7171"
|
||||
* "adi,ad7780"
|
||||
* "adi,ad7781"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: the regulator supply for the ADC reference voltage
|
||||
|
||||
Optional properties:
|
||||
|
||||
- powerdown-gpios: must be the device tree identifier of the PDRST pin. If
|
||||
specified, it will be asserted during driver probe. As the
|
||||
line is active high, it should be marked GPIO_ACTIVE_HIGH.
|
||||
- adi,gain-gpios: must be the device tree identifier of the GAIN pin. Only for
|
||||
the ad778x chips. If specified, it will be asserted during
|
||||
driver probe. As the line is active low, it should be marked
|
||||
GPIO_ACTIVE_LOW.
|
||||
- adi,filter-gpios: must be the device tree identifier of the FILTER pin. Only
|
||||
for the ad778x chips. If specified, it will be asserted
|
||||
during driver probe. As the line is active low, it should be
|
||||
marked GPIO_ACTIVE_LOW.
|
||||
|
||||
Example:
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7780";
|
||||
reg = <0>;
|
||||
vref-supply = <&vdd_supply>
|
||||
|
||||
powerdown-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>;
|
||||
adi,gain-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
|
||||
adi,filter-gpios = <&gpio 15 GPIO_ACTIVE_LOW>;
|
||||
};
|
|
@ -9,6 +9,7 @@ Required properties:
|
|||
- "amlogic,meson-gxl-saradc" for GXL
|
||||
- "amlogic,meson-gxm-saradc" for GXM
|
||||
- "amlogic,meson-axg-saradc" for AXG
|
||||
- "amlogic,meson-g12a-saradc" for AXG
|
||||
along with the generic "amlogic,meson-saradc"
|
||||
- reg: the physical base address and length of the registers
|
||||
- interrupts: the interrupt indicating end of sampling
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
* AVIA HX711 ADC chip for weight cells
|
||||
Bit-banging driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "avia,hx711"
|
||||
- sck-gpios: Definition of the GPIO for the clock
|
||||
- dout-gpios: Definition of the GPIO for data-out
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt
|
||||
- avdd-supply: Definition of the regulator used as analog supply
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: Frequency of PD_SCK in Hz
|
||||
Minimum value allowed is 10 kHz because of maximum
|
||||
high time of 50 microseconds.
|
||||
|
||||
Example:
|
||||
weight {
|
||||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
avdd-suppy = <&avdd>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/iio/adc/avia-hx711.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: AVIA HX711 ADC chip for weight cells
|
||||
|
||||
maintainers:
|
||||
- Andreas Klinger <ak@it-klinger.de>
|
||||
|
||||
description: |
|
||||
Bit-banging driver using two GPIOs:
|
||||
- sck-gpio gives a clock to the sensor with 24 cycles for data retrieval
|
||||
and up to 3 cycles for selection of the input channel and gain for the
|
||||
next measurement
|
||||
- dout-gpio is the sensor data the sensor responds to the clock
|
||||
|
||||
Specifications about the driver can be found at:
|
||||
http://www.aviaic.com/ENProducts.aspx
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- avia,hx711
|
||||
|
||||
sck-gpios:
|
||||
description:
|
||||
Definition of the GPIO for the clock (output). In the datasheet it is
|
||||
named PD_SCK
|
||||
maxItems: 1
|
||||
|
||||
dout-gpios:
|
||||
description:
|
||||
Definition of the GPIO for the data-out sent by the sensor in
|
||||
response to the clock (input).
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt for information
|
||||
on how to specify a consumer gpio.
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description:
|
||||
Definition of the regulator used as analog supply
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
minimum: 20000
|
||||
maximum: 2500000
|
||||
default: 400000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- sck-gpios
|
||||
- dout-gpios
|
||||
- avdd-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
weight {
|
||||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
avdd-suppy = <&avdd>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
|
@ -6,6 +6,10 @@ Required properties:
|
|||
region.
|
||||
- interrupts: The ADC interrupt
|
||||
|
||||
Optional:
|
||||
- vref-supply: The regulator supply ADC reference voltage, optional
|
||||
for legacy reason, but highly encouraging to us in new device tree
|
||||
|
||||
Example:
|
||||
|
||||
adc@40048000 {
|
||||
|
@ -13,4 +17,5 @@ Example:
|
|||
reg = <0x40048000 0x1000>;
|
||||
interrupt-parent = <&mic>;
|
||||
interrupts = <39 0>;
|
||||
vref-supply = <&vcc>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
* Texas Instruments ADS8344 A/DC chip
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "ti,ads8344"
|
||||
- reg: SPI chip select number for the device
|
||||
- vref-supply: phandle to a regulator node that supplies the
|
||||
reference voltage
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,ads8344";
|
||||
reg = <0>;
|
||||
vref-supply = <&refin_supply>;
|
||||
spi-max-frequency = <10000000>;
|
||||
};
|
|
@ -1,7 +1,13 @@
|
|||
* Plantower PMS7003 particulate matter sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "plantower,pms7003"
|
||||
- compatible: must one of:
|
||||
"plantower,pms1003"
|
||||
"plantower,pms3003"
|
||||
"plantower,pms5003"
|
||||
"plantower,pms6003"
|
||||
"plantower,pms7003"
|
||||
"plantower,pmsa003"
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor
|
||||
|
||||
Optional properties:
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
* Bosch BMG160 triaxial rotation sensor (gyroscope)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "bosch,bmg160" or "bosch,bmi055_gyro"
|
||||
- reg : the I2C address of the sensor (0x69)
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts : interrupt mapping for GPIO IRQ, it should by configured with
|
||||
flags IRQ_TYPE_EDGE_RISING
|
||||
|
||||
Example:
|
||||
|
||||
bmg160@69 {
|
||||
compatible = "bosch,bmg160";
|
||||
reg = <0x69>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <18 (IRQ_TYPE_EDGE_RISING)>;
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
* NXP FXAS21002C Gyroscope device tree bindings
|
||||
|
||||
http://www.nxp.com/products/sensors/gyroscopes/3-axis-digital-gyroscope:FXAS21002C
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "nxp,fxas21002c"
|
||||
- reg : the I2C address of the sensor or SPI chip select number for the
|
||||
device.
|
||||
- vdd-supply: phandle to the regulator that provides power to the sensor.
|
||||
- vddio-supply: phandle to the regulator that provides power to the bus.
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios : gpio used to reset the device, see gpio/gpio.txt
|
||||
- interrupts : device support 2 interrupts, INT1 and INT2,
|
||||
the interrupts can be triggered on rising or falling edges.
|
||||
See interrupt-controller/interrupts.txt
|
||||
- interrupt-names: should contain "INT1" or "INT2", the gyroscope interrupt
|
||||
line in use.
|
||||
- drive-open-drain: the interrupt/data ready line will be configured
|
||||
as open drain, which is useful if several sensors share
|
||||
the same interrupt line. This is a boolean property.
|
||||
(This binding is taken from pinctrl/pinctrl-bindings.txt)
|
||||
|
||||
Example:
|
||||
|
||||
gyroscope@20 {
|
||||
compatible = "nxp,fxas21002c";
|
||||
reg = <0x20>;
|
||||
vdd-supply = <®_peri_3p15v>;
|
||||
vddio-supply = <®_peri_3p15v>;
|
||||
};
|
|
@ -0,0 +1,85 @@
|
|||
|
||||
Analog Devices ADIS16480 and similar IMUs
|
||||
|
||||
Required properties for the ADIS16480:
|
||||
|
||||
- compatible: Must be one of
|
||||
* "adi,adis16375"
|
||||
* "adi,adis16480"
|
||||
* "adi,adis16485"
|
||||
* "adi,adis16488"
|
||||
* "adi,adis16495-1"
|
||||
* "adi,adis16495-2"
|
||||
* "adi,adis16495-3"
|
||||
* "adi,adis16497-1"
|
||||
* "adi,adis16497-2"
|
||||
* "adi,adis16497-3"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- spi-cpha: See Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- spi-cpol: See Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- interrupts: interrupt mapping for IRQ, accepted values are:
|
||||
* IRQF_TRIGGER_RISING
|
||||
* IRQF_TRIGGER_FALLING
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-names: Data ready line selection. Valid values are:
|
||||
* DIO1
|
||||
* DIO2
|
||||
* DIO3
|
||||
* DIO4
|
||||
If this field is left empty, DIO1 is assigned as default data ready
|
||||
signal.
|
||||
- reset-gpios: must be the device tree identifier of the RESET pin. As the line
|
||||
is active low, it should be marked GPIO_ACTIVE_LOW.
|
||||
- clocks: phandle to the external clock. Should be set according to
|
||||
"clock-names".
|
||||
If this field is left empty together with the "clock-names" field, then
|
||||
the internal clock is used.
|
||||
- clock-names: The name of the external clock to be used. Valid values are:
|
||||
* sync: In sync mode, the internal clock is disabled and the frequency
|
||||
of the external clock signal establishes therate of data
|
||||
collection and processing. See Fig 14 and 15 in the datasheet.
|
||||
The clock-frequency must be:
|
||||
* 3000 to 4500 Hz for adis1649x devices.
|
||||
* 700 to 2400 Hz for adis1648x devices.
|
||||
* pps: In Pulse Per Second (PPS) Mode, the rate of data collection and
|
||||
production is equal to the product of the external clock
|
||||
frequency and the scale factor in the SYNC_SCALE register, see
|
||||
Table 154 in the datasheet.
|
||||
The clock-frequency must be:
|
||||
* 1 to 128 Hz for adis1649x devices.
|
||||
* This mode is not supported by adis1648x devices.
|
||||
If this field is left empty together with the "clocks" field, then the
|
||||
internal clock is used.
|
||||
- adi,ext-clk-pin: The DIOx line to be used as an external clock input.
|
||||
Valid values are:
|
||||
* DIO1
|
||||
* DIO2
|
||||
* DIO3
|
||||
* DIO4
|
||||
Each DIOx pin supports only one function at a time (data ready line
|
||||
selection or external clock input). When a single pin has two
|
||||
two assignments, the enable bit for the lower priority function
|
||||
automatically resets to zero (disabling the lower priority function).
|
||||
Data ready has highest priority.
|
||||
If this field is left empty, DIO2 is assigned as default external clock
|
||||
input pin.
|
||||
|
||||
Example:
|
||||
|
||||
imu@0 {
|
||||
compatible = "adi,adis16495-1";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <3200000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupts = <25 IRQF_TRIGGER_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupt-names = "DIO2";
|
||||
clocks = <&adis16495_sync>;
|
||||
clock-names = "sync";
|
||||
adi,ext-clk-pin = "DIO1";
|
||||
};
|
|
@ -8,6 +8,9 @@ Required properties:
|
|||
"st,lsm6dsm"
|
||||
"st,ism330dlc"
|
||||
"st,lsm6dso"
|
||||
"st,asm330lhh"
|
||||
"st,lsm6dsox"
|
||||
"st,lsm6dsr"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
VISHAY VCNL4000 - Ambient Light and proximity sensor
|
||||
|
||||
This driver supports the VCNL4000/10/20/40 and VCNL4200 chips
|
||||
|
||||
Required properties:
|
||||
|
||||
-compatible: must be one of :
|
||||
vishay,vcnl4000
|
||||
vishay,vcnl4010
|
||||
vishay,vcnl4020
|
||||
vishay,vcnl4040
|
||||
vishay,vcnl4200
|
||||
|
||||
-reg: I2C address of the sensor, should be one from below based on the model:
|
||||
0x13
|
||||
0x51
|
||||
0x60
|
||||
|
||||
Example:
|
||||
|
||||
light-sensor@51 {
|
||||
compatible = "vishay,vcnl4200";
|
||||
reg = <0x51>;
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
BMP085/BMP18x/BMP28x digital pressure sensors
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of:
|
||||
"bosch,bmp085"
|
||||
"bosch,bmp180"
|
||||
"bosch,bmp280"
|
||||
"bosch,bme280"
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt mapping for IRQ
|
||||
- reset-gpios: a GPIO line handling reset of the sensor: as the line is
|
||||
active low, it should be marked GPIO_ACTIVE_LOW (see gpio/gpio.txt)
|
||||
- vddd-supply: digital voltage regulator (see regulator/regulator.txt)
|
||||
- vdda-supply: analog voltage regulator (see regulator/regulator.txt)
|
||||
|
||||
Example:
|
||||
|
||||
pressure@77 {
|
||||
compatible = "bosch,bmp085";
|
||||
reg = <0x77>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
|
||||
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
|
||||
vddd-supply = <&foo>;
|
||||
vdda-supply = <&bar>;
|
||||
};
|
До Ширина: | Высота: | Размер: 745 B |
|
@ -0,0 +1,70 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/pressure/bmp085.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: BMP085/BMP180/BMP280/BME280 pressure iio sensors
|
||||
|
||||
maintainers:
|
||||
- Andreas Klinger <ak@it-klinger.de>
|
||||
|
||||
description: |
|
||||
Pressure, temperature and humidity iio sensors with i2c and spi interfaces
|
||||
|
||||
Specifications about the sensor can be found at:
|
||||
https://www.bosch-sensortec.com/bst/products/all_products/bmp180
|
||||
https://www.bosch-sensortec.com/bst/products/all_products/bmp280
|
||||
https://www.bosch-sensortec.com/bst/products/all_products/bme280
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- bosch,bmp085
|
||||
- bosch,bmp180
|
||||
- bosch,bmp280
|
||||
- bosch,bme280
|
||||
|
||||
vddd-supply:
|
||||
description:
|
||||
digital voltage regulator (see regulator/regulator.txt)
|
||||
maxItems: 1
|
||||
|
||||
vdda-supply:
|
||||
description:
|
||||
analog voltage regulator (see regulator/regulator.txt)
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
A GPIO line handling reset of the sensor. As the line is active low,
|
||||
it should be marked GPIO_ACTIVE_LOW (see gpio/gpio.txt)
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
interrupt mapping for IRQ (BMP085 only)
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vddd-supply
|
||||
- vdda-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pressure@77 {
|
||||
compatible = "bosch,bmp085";
|
||||
reg = <0x77>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
|
||||
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
|
||||
vddd-supply = <&foo>;
|
||||
vdda-supply = <&bar>;
|
||||
};
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
* Devantech SRF04 ultrasonic range finder
|
||||
Bit-banging driver using two GPIOs
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "devantech,srf04"
|
||||
|
||||
- trig-gpios: Definition of the GPIO for the triggering (output)
|
||||
This GPIO is set for about 10 us by the driver to tell the
|
||||
device it should initiate the measurement cycle.
|
||||
|
||||
- echo-gpios: Definition of the GPIO for the echo (input)
|
||||
This GPIO is set by the device as soon as an ultrasonic
|
||||
burst is sent out and reset when the first echo is
|
||||
received.
|
||||
Thus this GPIO is set while the ultrasonic waves are doing
|
||||
one round trip.
|
||||
It needs to be an GPIO which is able to deliver an
|
||||
interrupt because the time between two interrupts is
|
||||
measured in the driver.
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt for
|
||||
information on how to specify a consumer gpio.
|
||||
|
||||
Example:
|
||||
srf04@0 {
|
||||
compatible = "devantech,srf04";
|
||||
trig-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||
echo-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/proximity/devantech-srf04.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Devantech SRF04 and Maxbotix mb1000 ultrasonic range finder
|
||||
|
||||
maintainers:
|
||||
- Andreas Klinger <ak@it-klinger.de>
|
||||
|
||||
description: |
|
||||
Bit-banging driver using two GPIOs:
|
||||
- trigger-gpio is raised by the driver to start sending out an ultrasonic
|
||||
burst
|
||||
- echo-gpio is held high by the sensor after sending ultrasonic burst
|
||||
until it is received once again
|
||||
|
||||
Specifications about the devices can be found at:
|
||||
http://www.robot-electronics.co.uk/htm/srf04tech.htm
|
||||
|
||||
http://www.maxbotix.com/documents/LV-MaxSonar-EZ_Datasheet.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- devantech,srf04
|
||||
- maxbotix,mb1000
|
||||
- maxbotix,mb1010
|
||||
- maxbotix,mb1020
|
||||
- maxbotix,mb1030
|
||||
- maxbotix,mb1040
|
||||
|
||||
trig-gpios:
|
||||
description:
|
||||
Definition of the GPIO for the triggering (output)
|
||||
This GPIO is set for about 10 us by the driver to tell the device it
|
||||
should initiate the measurement cycle.
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt for information
|
||||
on how to specify a consumer gpio.
|
||||
maxItems: 1
|
||||
|
||||
echo-gpios:
|
||||
description:
|
||||
Definition of the GPIO for the echo (input)
|
||||
This GPIO is set by the device as soon as an ultrasonic burst is sent
|
||||
out and reset when the first echo is received.
|
||||
Thus this GPIO is set while the ultrasonic waves are doing one round
|
||||
trip.
|
||||
It needs to be an GPIO which is able to deliver an interrupt because
|
||||
the time between two interrupts is measured in the driver.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- trig-gpios
|
||||
- echo-gpios
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
proximity {
|
||||
compatible = "devantech,srf04";
|
||||
trig-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||
echo-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
* MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202,
|
||||
mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface
|
||||
for ranging
|
||||
|
||||
Required properties:
|
||||
- compatible: "maxbotix,mb1202",
|
||||
"maxbotix,mb1212",
|
||||
"maxbotix,mb1222",
|
||||
"maxbotix,mb1232",
|
||||
"maxbotix,mb1242",
|
||||
"maxbotix,mb7040" or
|
||||
"maxbotix,mb7137"
|
||||
|
||||
- reg: i2c address of the device, see also i2c/i2c.txt
|
||||
|
||||
Optional properties:
|
||||
- interrupts: Interrupt used to announce the preceding reading
|
||||
request has finished and that data is available.
|
||||
If no interrupt is specified the device driver
|
||||
falls back to wait a fixed amount of time until
|
||||
data can be retrieved.
|
||||
|
||||
Example:
|
||||
proximity@70 {
|
||||
compatible = "maxbotix,mb1232";
|
||||
reg = <0x70>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
|
@ -49,6 +49,7 @@ Accelerometers:
|
|||
- st,lis2dw12
|
||||
- st,lis3dhh
|
||||
- st,lis3de
|
||||
- st,lis2de12
|
||||
|
||||
Gyroscopes:
|
||||
- st,l3g4200d-gyro
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
Maxim MAX31856 thermocouple support
|
||||
|
||||
https://datasheets.maximintegrated.com/en/ds/MAX31856.pdf
|
||||
|
||||
Optional property:
|
||||
- thermocouple-type: Type of thermocouple (THERMOCOUPLE_TYPE_K if
|
||||
omitted). Supported types are B, E, J, K, N, R, S, T.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "maxim,max31856"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: As per datasheet max. supported freq is 5000000
|
||||
- spi-cpha: must be defined for max31856 to enable SPI mode 1
|
||||
|
||||
Refer to spi/spi-bus.txt for generic SPI slave bindings.
|
||||
|
||||
Example:
|
||||
temp-sensor@0 {
|
||||
compatible = "maxim,max31856";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpha;
|
||||
thermocouple-type = <THERMOCOUPLE_TYPE_K>;
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
If the temperature sensor device can be configured to use some specific
|
||||
thermocouple type, you can use the defined types provided in the file
|
||||
"include/dt-bindings/iio/temperature/thermocouple.h".
|
||||
|
||||
Property:
|
||||
thermocouple-type: A single cell representing the type of the thermocouple
|
||||
used by the device.
|
|
@ -16,7 +16,7 @@ Required properties:
|
|||
|
||||
Optional subnodes:
|
||||
- pwm: See ../pwm/pwm-stm32-lp.txt
|
||||
- counter: See ../iio/timer/stm32-lptimer-cnt.txt
|
||||
- counter: See ../counter/stm32-lptimer-cnt.txt
|
||||
- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
|
||||
|
||||
Example:
|
||||
|
|
|
@ -28,6 +28,7 @@ Optional parameters:
|
|||
Optional subnodes:
|
||||
- pwm: See ../pwm/pwm-stm32.txt
|
||||
- timer: See ../iio/timer/stm32-timer-trigger.txt
|
||||
- counter: See ../counter/stm32-timer-cnt.txt
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
|
@ -48,6 +49,12 @@ Example:
|
|||
compatible = "st,stm32-timer-trigger";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
counter {
|
||||
compatible = "st,stm32-timer-counter";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&tim1_in_pins>;
|
||||
};
|
||||
};
|
||||
|
||||
Example with all dmas:
|
||||
|
|
|
@ -36,6 +36,7 @@ aptina Aptina Imaging
|
|||
arasan Arasan Chip Systems
|
||||
archermind ArcherMind Technology (Nanjing) Co., Ltd.
|
||||
arctic Arctic Sand
|
||||
arcx arcx Inc. / Archronix Inc.
|
||||
aries Aries Embedded GmbH
|
||||
arm ARM Ltd.
|
||||
armadeus ARMadeus Systems SARL
|
||||
|
@ -210,6 +211,7 @@ kiebackpeter Kieback & Peter GmbH
|
|||
kinetic Kinetic Technologies
|
||||
kingdisplay King & Display Technology Co., Ltd.
|
||||
kingnovel Kingnovel Technology Co., Ltd.
|
||||
kionix Kionix, Inc.
|
||||
koe Kaohsiung Opto-Electronics Inc.
|
||||
kosagi Sutajio Ko-Usagi PTE Ltd.
|
||||
kyo Kyocera Corporation
|
||||
|
@ -233,6 +235,7 @@ lsi LSI Corp. (LSI Logic)
|
|||
lwn Liebherr-Werk Nenzing GmbH
|
||||
macnica Macnica Americas
|
||||
marvell Marvell Technology Group Ltd.
|
||||
maxbotix MaxBotix Inc.
|
||||
maxim Maxim Integrated Products
|
||||
mbvl Mobiveil Inc.
|
||||
mcube mCube
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=========================
|
||||
Generic Counter Interface
|
||||
=========================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Counter devices are prevalent within a diverse spectrum of industries.
|
||||
The ubiquitous presence of these devices necessitates a common interface
|
||||
and standard of interaction and exposure. This driver API attempts to
|
||||
resolve the issue of duplicate code found among existing counter device
|
||||
drivers by introducing a generic counter interface for consumption. The
|
||||
Generic Counter interface enables drivers to support and expose a common
|
||||
set of components and functionality present in counter devices.
|
||||
|
||||
Theory
|
||||
======
|
||||
|
||||
Counter devices can vary greatly in design, but regardless of whether
|
||||
some devices are quadrature encoder counters or tally counters, all
|
||||
counter devices consist of a core set of components. This core set of
|
||||
components, shared by all counter devices, is what forms the essence of
|
||||
the Generic Counter interface.
|
||||
|
||||
There are three core components to a counter:
|
||||
|
||||
* Count:
|
||||
Count data for a set of Signals.
|
||||
|
||||
* Signal:
|
||||
Input data that is evaluated by the counter to determine the count
|
||||
data.
|
||||
|
||||
* Synapse:
|
||||
The association of a Signal with a respective Count.
|
||||
|
||||
COUNT
|
||||
-----
|
||||
A Count represents the count data for a set of Signals. The Generic
|
||||
Counter interface provides the following available count data types:
|
||||
|
||||
* COUNT_POSITION:
|
||||
Unsigned integer value representing position.
|
||||
|
||||
A Count has a count function mode which represents the update behavior
|
||||
for the count data. The Generic Counter interface provides the following
|
||||
available count function modes:
|
||||
|
||||
* Increase:
|
||||
Accumulated count is incremented.
|
||||
|
||||
* Decrease:
|
||||
Accumulated count is decremented.
|
||||
|
||||
* Pulse-Direction:
|
||||
Rising edges on signal A updates the respective count. The input level
|
||||
of signal B determines direction.
|
||||
|
||||
* Quadrature:
|
||||
A pair of quadrature encoding signals are evaluated to determine
|
||||
position and direction. The following Quadrature modes are available:
|
||||
|
||||
- x1 A:
|
||||
If direction is forward, rising edges on quadrature pair signal A
|
||||
updates the respective count; if the direction is backward, falling
|
||||
edges on quadrature pair signal A updates the respective count.
|
||||
Quadrature encoding determines the direction.
|
||||
|
||||
- x1 B:
|
||||
If direction is forward, rising edges on quadrature pair signal B
|
||||
updates the respective count; if the direction is backward, falling
|
||||
edges on quadrature pair signal B updates the respective count.
|
||||
Quadrature encoding determines the direction.
|
||||
|
||||
- x2 A:
|
||||
Any state transition on quadrature pair signal A updates the
|
||||
respective count. Quadrature encoding determines the direction.
|
||||
|
||||
- x2 B:
|
||||
Any state transition on quadrature pair signal B updates the
|
||||
respective count. Quadrature encoding determines the direction.
|
||||
|
||||
- x4:
|
||||
Any state transition on either quadrature pair signals updates the
|
||||
respective count. Quadrature encoding determines the direction.
|
||||
|
||||
A Count has a set of one or more associated Signals.
|
||||
|
||||
SIGNAL
|
||||
------
|
||||
A Signal represents a counter input data; this is the input data that is
|
||||
evaluated by the counter to determine the count data; e.g. a quadrature
|
||||
signal output line of a rotary encoder. Not all counter devices provide
|
||||
user access to the Signal data.
|
||||
|
||||
The Generic Counter interface provides the following available signal
|
||||
data types for when the Signal data is available for user access:
|
||||
|
||||
* SIGNAL_LEVEL:
|
||||
Signal line state level. The following states are possible:
|
||||
|
||||
- SIGNAL_LEVEL_LOW:
|
||||
Signal line is in a low state.
|
||||
|
||||
- SIGNAL_LEVEL_HIGH:
|
||||
Signal line is in a high state.
|
||||
|
||||
A Signal may be associated with one or more Counts.
|
||||
|
||||
SYNAPSE
|
||||
-------
|
||||
A Synapse represents the association of a Signal with a respective
|
||||
Count. Signal data affects respective Count data, and the Synapse
|
||||
represents this relationship.
|
||||
|
||||
The Synapse action mode specifies the Signal data condition which
|
||||
triggers the respective Count's count function evaluation to update the
|
||||
count data. The Generic Counter interface provides the following
|
||||
available action modes:
|
||||
|
||||
* None:
|
||||
Signal does not trigger the count function. In Pulse-Direction count
|
||||
function mode, this Signal is evaluated as Direction.
|
||||
|
||||
* Rising Edge:
|
||||
Low state transitions to high state.
|
||||
|
||||
* Falling Edge:
|
||||
High state transitions to low state.
|
||||
|
||||
* Both Edges:
|
||||
Any state transition.
|
||||
|
||||
A counter is defined as a set of input signals associated with count
|
||||
data that are generated by the evaluation of the state of the associated
|
||||
input signals as defined by the respective count functions. Within the
|
||||
context of the Generic Counter interface, a counter consists of Counts
|
||||
each associated with a set of Signals, whose respective Synapse
|
||||
instances represent the count function update conditions for the
|
||||
associated Counts.
|
||||
|
||||
Paradigm
|
||||
========
|
||||
|
||||
The most basic counter device may be expressed as a single Count
|
||||
associated with a single Signal via a single Synapse. Take for example
|
||||
a counter device which simply accumulates a count of rising edges on a
|
||||
source input line::
|
||||
|
||||
Count Synapse Signal
|
||||
----- ------- ------
|
||||
+---------------------+
|
||||
| Data: Count | Rising Edge ________
|
||||
| Function: Increase | <------------- / Source \
|
||||
| | ____________
|
||||
+---------------------+
|
||||
|
||||
In this example, the Signal is a source input line with a pulsing
|
||||
voltage, while the Count is a persistent count value which is repeatedly
|
||||
incremented. The Signal is associated with the respective Count via a
|
||||
Synapse. The increase function is triggered by the Signal data condition
|
||||
specified by the Synapse -- in this case a rising edge condition on the
|
||||
voltage input line. In summary, the counter device existence and
|
||||
behavior is aptly represented by respective Count, Signal, and Synapse
|
||||
components: a rising edge condition triggers an increase function on an
|
||||
accumulating count datum.
|
||||
|
||||
A counter device is not limited to a single Signal; in fact, in theory
|
||||
many Signals may be associated with even a single Count. For example, a
|
||||
quadrature encoder counter device can keep track of position based on
|
||||
the states of two input lines::
|
||||
|
||||
Count Synapse Signal
|
||||
----- ------- ------
|
||||
+-------------------------+
|
||||
| Data: Position | Both Edges ___
|
||||
| Function: Quadrature x4 | <------------ / A \
|
||||
| | _______
|
||||
| |
|
||||
| | Both Edges ___
|
||||
| | <------------ / B \
|
||||
| | _______
|
||||
+-------------------------+
|
||||
|
||||
In this example, two Signals (quadrature encoder lines A and B) are
|
||||
associated with a single Count: a rising or falling edge on either A or
|
||||
B triggers the "Quadrature x4" function which determines the direction
|
||||
of movement and updates the respective position data. The "Quadrature
|
||||
x4" function is likely implemented in the hardware of the quadrature
|
||||
encoder counter device; the Count, Signals, and Synapses simply
|
||||
represent this hardware behavior and functionality.
|
||||
|
||||
Signals associated with the same Count can have differing Synapse action
|
||||
mode conditions. For example, a quadrature encoder counter device
|
||||
operating in a non-quadrature Pulse-Direction mode could have one input
|
||||
line dedicated for movement and a second input line dedicated for
|
||||
direction::
|
||||
|
||||
Count Synapse Signal
|
||||
----- ------- ------
|
||||
+---------------------------+
|
||||
| Data: Position | Rising Edge ___
|
||||
| Function: Pulse-Direction | <------------- / A \ (Movement)
|
||||
| | _______
|
||||
| |
|
||||
| | None ___
|
||||
| | <------------- / B \ (Direction)
|
||||
| | _______
|
||||
+---------------------------+
|
||||
|
||||
Only Signal A triggers the "Pulse-Direction" update function, but the
|
||||
instantaneous state of Signal B is still required in order to know the
|
||||
direction so that the position data may be properly updated. Ultimately,
|
||||
both Signals are associated with the same Count via two respective
|
||||
Synapses, but only one Synapse has an active action mode condition which
|
||||
triggers the respective count function while the other is left with a
|
||||
"None" condition action mode to indicate its respective Signal's
|
||||
availability for state evaluation despite its non-triggering mode.
|
||||
|
||||
Keep in mind that the Signal, Synapse, and Count are abstract
|
||||
representations which do not need to be closely married to their
|
||||
respective physical sources. This allows the user of a counter to
|
||||
divorce themselves from the nuances of physical components (such as
|
||||
whether an input line is differential or single-ended) and instead focus
|
||||
on the core idea of what the data and process represent (e.g. position
|
||||
as interpreted from quadrature encoding data).
|
||||
|
||||
Userspace Interface
|
||||
===================
|
||||
|
||||
Several sysfs attributes are generated by the Generic Counter interface,
|
||||
and reside under the /sys/bus/counter/devices/counterX directory, where
|
||||
counterX refers to the respective counter device. Please see
|
||||
Documentation/ABI/testing/sys-bus-counter-generic-sysfs for detailed
|
||||
information on each Generic Counter interface sysfs attribute.
|
||||
|
||||
Through these sysfs attributes, programs and scripts may interact with
|
||||
the Generic Counter paradigm Counts, Signals, and Synapses of respective
|
||||
counter devices.
|
||||
|
||||
Driver API
|
||||
==========
|
||||
|
||||
Driver authors may utilize the Generic Counter interface in their code
|
||||
by including the include/linux/counter.h header file. This header file
|
||||
provides several core data structures, function prototypes, and macros
|
||||
for defining a counter device.
|
||||
|
||||
.. kernel-doc:: include/linux/counter.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/counter/generic-counter.c
|
||||
:export:
|
||||
|
||||
Implementation
|
||||
==============
|
||||
|
||||
To support a counter device, a driver must first allocate the available
|
||||
Counter Signals via counter_signal structures. These Signals should
|
||||
be stored as an array and set to the signals array member of an
|
||||
allocated counter_device structure before the Counter is registered to
|
||||
the system.
|
||||
|
||||
Counter Counts may be allocated via counter_count structures, and
|
||||
respective Counter Signal associations (Synapses) made via
|
||||
counter_synapse structures. Associated counter_synapse structures are
|
||||
stored as an array and set to the the synapses array member of the
|
||||
respective counter_count structure. These counter_count structures are
|
||||
set to the counts array member of an allocated counter_device structure
|
||||
before the Counter is registered to the system.
|
||||
|
||||
Driver callbacks should be provided to the counter_device structure via
|
||||
a constant counter_ops structure in order to communicate with the
|
||||
device: to read and write various Signals and Counts, and to set and get
|
||||
the "action mode" and "function mode" for various Synapses and Counts
|
||||
respectively.
|
||||
|
||||
A defined counter_device structure may be registered to the system by
|
||||
passing it to the counter_register function, and unregistered by passing
|
||||
it to the counter_unregister function. Similarly, the
|
||||
devm_counter_register and devm_counter_unregister functions may be used
|
||||
if device memory-managed registration is desired.
|
||||
|
||||
Extension sysfs attributes can be created for auxiliary functionality
|
||||
and data by passing in defined counter_device_ext, counter_count_ext,
|
||||
and counter_signal_ext structures. In these cases, the
|
||||
counter_device_ext structure is used for global configuration of the
|
||||
respective Counter device, while the counter_count_ext and
|
||||
counter_signal_ext structures allow for auxiliary exposure and
|
||||
configuration of a specific Count or Signal respectively.
|
||||
|
||||
Architecture
|
||||
============
|
||||
|
||||
When the Generic Counter interface counter module is loaded, the
|
||||
counter_init function is called which registers a bus_type named
|
||||
"counter" to the system. Subsequently, when the module is unloaded, the
|
||||
counter_exit function is called which unregisters the bus_type named
|
||||
"counter" from the system.
|
||||
|
||||
Counter devices are registered to the system via the counter_register
|
||||
function, and later removed via the counter_unregister function. The
|
||||
counter_register function establishes a unique ID for the Counter
|
||||
device and creates a respective sysfs directory, where X is the
|
||||
mentioned unique ID:
|
||||
|
||||
/sys/bus/counter/devices/counterX
|
||||
|
||||
Sysfs attributes are created within the counterX directory to expose
|
||||
functionality, configurations, and data relating to the Counts, Signals,
|
||||
and Synapses of the Counter device, as well as options and information
|
||||
for the Counter device itself.
|
||||
|
||||
Each Signal has a directory created to house its relevant sysfs
|
||||
attributes, where Y is the unique ID of the respective Signal:
|
||||
|
||||
/sys/bus/counter/devices/counterX/signalY
|
||||
|
||||
Similarly, each Count has a directory created to house its relevant
|
||||
sysfs attributes, where Y is the unique ID of the respective Count:
|
||||
|
||||
/sys/bus/counter/devices/counterX/countY
|
||||
|
||||
For a more detailed breakdown of the available Generic Counter interface
|
||||
sysfs attributes, please refer to the
|
||||
Documentation/ABI/testing/sys-bus-counter file.
|
||||
|
||||
The Signals and Counts associated with the Counter device are registered
|
||||
to the system as well by the counter_register function. The
|
||||
signal_read/signal_write driver callbacks are associated with their
|
||||
respective Signal attributes, while the count_read/count_write and
|
||||
function_get/function_set driver callbacks are associated with their
|
||||
respective Count attributes; similarly, the same is true for the
|
||||
action_get/action_set driver callbacks and their respective Synapse
|
||||
attributes. If a driver callback is left undefined, then the respective
|
||||
read/write permission is left disabled for the relevant attributes.
|
||||
|
||||
Similarly, extension sysfs attributes are created for the defined
|
||||
counter_device_ext, counter_count_ext, and counter_signal_ext
|
||||
structures that are passed in.
|
|
@ -57,6 +57,7 @@ available subsections can be seen below.
|
|||
soundwire/index
|
||||
fpga/index
|
||||
acpi/index
|
||||
generic-counter
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
|
35
MAINTAINERS
35
MAINTAINERS
|
@ -268,12 +268,13 @@ L: linux-gpio@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/gpio/gpio-104-idio-16.c
|
||||
|
||||
ACCES 104-QUAD-8 IIO DRIVER
|
||||
ACCES 104-QUAD-8 DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8
|
||||
F: drivers/iio/counter/104-quad-8.c
|
||||
F: drivers/counter/104-quad-8.c
|
||||
|
||||
ACCES PCI-IDIO-16 GPIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
|
@ -868,7 +869,7 @@ L: linux-iio@vger.kernel.org
|
|||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7606.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/ad7606.txt
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt
|
||||
|
||||
ANALOG DEVICES INC AD7768-1 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
|
@ -950,6 +951,7 @@ F: drivers/dma/dma-axi-dmac.c
|
|||
ANALOG DEVICES INC IIO DRIVERS
|
||||
M: Lars-Peter Clausen <lars@metafoo.de>
|
||||
M: Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
W: http://wiki.analog.com/
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
|
@ -4057,6 +4059,16 @@ W: http://www.fi.muni.cz/~kas/cosa/
|
|||
S: Maintained
|
||||
F: drivers/net/wan/cosa*
|
||||
|
||||
COUNTER SUBSYSTEM
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-counter*
|
||||
F: Documentation/driver-api/generic-counter.rst
|
||||
F: drivers/counter/
|
||||
F: include/linux/counter.h
|
||||
F: include/linux/counter_enum.h
|
||||
|
||||
CPMAC ETHERNET DRIVER
|
||||
M: Florian Fainelli <f.fainelli@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
@ -9417,6 +9429,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/sound/max9860.txt
|
||||
F: sound/soc/codecs/max9860.*
|
||||
|
||||
MAXBOTIX ULTRASONIC RANGER IIO DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt
|
||||
F: drivers/iio/proximity/mb1232.c
|
||||
|
||||
MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER
|
||||
M: Javier Martinez Canillas <javier@dowhile0.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
@ -11133,6 +11152,16 @@ F: Documentation/ABI/stable/sysfs-bus-nvmem
|
|||
F: include/linux/nvmem-consumer.h
|
||||
F: include/linux/nvmem-provider.h
|
||||
|
||||
NXP FXAS21002C DRIVER
|
||||
M: Rui Miguel Silva <rmfrfs@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/gyroscope/fxas21002c.txt
|
||||
F: drivers/iio/gyro/fxas21002c_core.c
|
||||
F: drivers/iio/gyro/fxas21002c.h
|
||||
F: drivers/iio/gyro/fxas21002c_i2c.c
|
||||
F: drivers/iio/gyro/fxas21002c_spi.c
|
||||
|
||||
NXP SGTL5000 DRIVER
|
||||
M: Fabio Estevam <festevam@gmail.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
|
|
@ -446,6 +446,34 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
counter0: counter@29d0000 {
|
||||
compatible = "fsl,ftm-quaddec";
|
||||
reg = <0x0 0x29d0000 0x0 0x10000>;
|
||||
big-endian;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
counter1: counter@29e0000 {
|
||||
compatible = "fsl,ftm-quaddec";
|
||||
reg = <0x0 0x29e0000 0x0 0x10000>;
|
||||
big-endian;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
counter2: counter@29f0000 {
|
||||
compatible = "fsl,ftm-quaddec";
|
||||
reg = <0x0 0x29f0000 0x0 0x10000>;
|
||||
big-endian;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
counter3: counter@2a00000 {
|
||||
compatible = "fsl,ftm-quaddec";
|
||||
reg = <0x0 0x2a00000 0x0 0x10000>;
|
||||
big-endian;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
gpio0: gpio@2300000 {
|
||||
compatible = "fsl,ls1021a-gpio", "fsl,qoriq-gpio";
|
||||
reg = <0x0 0x2300000 0x0 0x10000>;
|
||||
|
|
|
@ -230,4 +230,6 @@ source "drivers/slimbus/Kconfig"
|
|||
|
||||
source "drivers/interconnect/Kconfig"
|
||||
|
||||
source "drivers/counter/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -187,3 +187,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
|
|||
obj-$(CONFIG_SIOX) += siox/
|
||||
obj-$(CONFIG_GNSS) += gnss/
|
||||
obj-$(CONFIG_INTERCONNECT) += interconnect/
|
||||
obj-$(CONFIG_COUNTER) += counter/
|
||||
|
|
|
@ -19,20 +19,9 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fsl/ftm.h>
|
||||
|
||||
#define FTM_SC 0x00
|
||||
#define FTM_SC_CLK_SHIFT 3
|
||||
#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
|
||||
#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
|
||||
#define FTM_SC_PS_MASK 0x7
|
||||
#define FTM_SC_TOIE BIT(6)
|
||||
#define FTM_SC_TOF BIT(7)
|
||||
|
||||
#define FTM_CNT 0x04
|
||||
#define FTM_MOD 0x08
|
||||
#define FTM_CNTIN 0x4C
|
||||
|
||||
#define FTM_PS_MAX 7
|
||||
#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT)
|
||||
|
||||
struct ftm_clock_device {
|
||||
void __iomem *clksrc_base;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,60 @@
|
|||
#
|
||||
# Counter devices
|
||||
#
|
||||
|
||||
menuconfig COUNTER
|
||||
tristate "Counter support"
|
||||
help
|
||||
This enables counter device support through the Generic Counter
|
||||
interface. You only need to enable this, if you also want to enable
|
||||
one or more of the counter device drivers below.
|
||||
|
||||
if COUNTER
|
||||
|
||||
config 104_QUAD_8
|
||||
tristate "ACCES 104-QUAD-8 driver"
|
||||
depends on PC104 && X86 && IIO
|
||||
select 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).
|
||||
|
||||
A counter's respective error flag may be cleared by performing a write
|
||||
operation on the respective count value attribute. Although the
|
||||
104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
|
||||
set, either directly or via the 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.
|
||||
|
||||
config STM32_TIMER_CNT
|
||||
tristate "STM32 Timer encoder counter driver"
|
||||
depends on MFD_STM32_TIMERS || COMPILE_TEST
|
||||
help
|
||||
Select this option to enable STM32 Timer quadrature encoder
|
||||
and counter driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stm32-timer-cnt.
|
||||
|
||||
config STM32_LPTIMER_CNT
|
||||
tristate "STM32 LP Timer encoder counter driver"
|
||||
depends on (MFD_STM32_LPTIMER || COMPILE_TEST) && IIO
|
||||
help
|
||||
Select this option to enable STM32 Low-Power Timer quadrature encoder
|
||||
and counter driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stm32-lptimer-cnt.
|
||||
|
||||
config FTM_QUADDEC
|
||||
tristate "Flex Timer Module Quadrature decoder driver"
|
||||
help
|
||||
Select this option to enable the Flex Timer Quadrature decoder
|
||||
driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ftm-quaddec.
|
||||
|
||||
endif # COUNTER
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for Counter devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_COUNTER) += counter.o
|
||||
|
||||
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
|
||||
obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
|
||||
obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
|
||||
obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,356 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Flex Timer Module Quadrature decoder
|
||||
*
|
||||
* This module implements a driver for decoding the FTM quadrature
|
||||
* of ex. a LS1021A
|
||||
*/
|
||||
|
||||
#include <linux/fsl/ftm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#define FTM_FIELD_UPDATE(ftm, offset, mask, val) \
|
||||
({ \
|
||||
uint32_t flags; \
|
||||
ftm_read(ftm, offset, &flags); \
|
||||
flags &= ~mask; \
|
||||
flags |= FIELD_PREP(mask, val); \
|
||||
ftm_write(ftm, offset, flags); \
|
||||
})
|
||||
|
||||
struct ftm_quaddec {
|
||||
struct counter_device counter;
|
||||
struct platform_device *pdev;
|
||||
void __iomem *ftm_base;
|
||||
bool big_endian;
|
||||
struct mutex ftm_quaddec_mutex;
|
||||
};
|
||||
|
||||
static void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
|
||||
{
|
||||
if (ftm->big_endian)
|
||||
*data = ioread32be(ftm->ftm_base + offset);
|
||||
else
|
||||
*data = ioread32(ftm->ftm_base + offset);
|
||||
}
|
||||
|
||||
static void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
|
||||
{
|
||||
if (ftm->big_endian)
|
||||
iowrite32be(data, ftm->ftm_base + offset);
|
||||
else
|
||||
iowrite32(data, ftm->ftm_base + offset);
|
||||
}
|
||||
|
||||
/* Hold mutex before modifying write protection state */
|
||||
static void ftm_clear_write_protection(struct ftm_quaddec *ftm)
|
||||
{
|
||||
uint32_t flag;
|
||||
|
||||
/* First see if it is enabled */
|
||||
ftm_read(ftm, FTM_FMS, &flag);
|
||||
|
||||
if (flag & FTM_FMS_WPEN)
|
||||
FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
|
||||
}
|
||||
|
||||
static void ftm_set_write_protection(struct ftm_quaddec *ftm)
|
||||
{
|
||||
FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
|
||||
}
|
||||
|
||||
static void ftm_reset_counter(struct ftm_quaddec *ftm)
|
||||
{
|
||||
/* Reset hardware counter to CNTIN */
|
||||
ftm_write(ftm, FTM_CNT, 0x0);
|
||||
}
|
||||
|
||||
static void ftm_quaddec_init(struct ftm_quaddec *ftm)
|
||||
{
|
||||
ftm_clear_write_protection(ftm);
|
||||
|
||||
/*
|
||||
* Do not write in the region from the CNTIN register through the
|
||||
* PWMLOAD register when FTMEN = 0.
|
||||
* Also reset other fields to zero
|
||||
*/
|
||||
ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
|
||||
ftm_write(ftm, FTM_CNTIN, 0x0000);
|
||||
ftm_write(ftm, FTM_MOD, 0xffff);
|
||||
ftm_write(ftm, FTM_CNT, 0x0);
|
||||
/* Set prescaler, reset other fields to zero */
|
||||
ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
|
||||
|
||||
/* Select quad mode, reset other fields to zero */
|
||||
ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
|
||||
|
||||
/* Unused features and reset to default section */
|
||||
ftm_write(ftm, FTM_POL, 0x0);
|
||||
ftm_write(ftm, FTM_FLTCTRL, 0x0);
|
||||
ftm_write(ftm, FTM_SYNCONF, 0x0);
|
||||
ftm_write(ftm, FTM_SYNC, 0xffff);
|
||||
|
||||
/* Lock the FTM */
|
||||
ftm_set_write_protection(ftm);
|
||||
}
|
||||
|
||||
static void ftm_quaddec_disable(struct ftm_quaddec *ftm)
|
||||
{
|
||||
ftm_clear_write_protection(ftm);
|
||||
ftm_write(ftm, FTM_MODE, 0);
|
||||
ftm_write(ftm, FTM_QDCTRL, 0);
|
||||
/*
|
||||
* This is enough to disable the counter. No clock has been
|
||||
* selected by writing to FTM_SC in init()
|
||||
*/
|
||||
ftm_set_write_protection(ftm);
|
||||
}
|
||||
|
||||
static int ftm_quaddec_get_prescaler(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *cnt_mode)
|
||||
{
|
||||
struct ftm_quaddec *ftm = counter->priv;
|
||||
uint32_t scflags;
|
||||
|
||||
ftm_read(ftm, FTM_SC, &scflags);
|
||||
|
||||
*cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_set_prescaler(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t cnt_mode)
|
||||
{
|
||||
struct ftm_quaddec *ftm = counter->priv;
|
||||
|
||||
mutex_lock(&ftm->ftm_quaddec_mutex);
|
||||
|
||||
ftm_clear_write_protection(ftm);
|
||||
FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
|
||||
ftm_set_write_protection(ftm);
|
||||
|
||||
/* Also resets the counter as it is undefined anyway now */
|
||||
ftm_reset_counter(ftm);
|
||||
|
||||
mutex_unlock(&ftm->ftm_quaddec_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const ftm_quaddec_prescaler[] = {
|
||||
"1", "2", "4", "8", "16", "32", "64", "128"
|
||||
};
|
||||
|
||||
static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
|
||||
.items = ftm_quaddec_prescaler,
|
||||
.num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
|
||||
.get = ftm_quaddec_get_prescaler,
|
||||
.set = ftm_quaddec_set_prescaler
|
||||
};
|
||||
|
||||
enum ftm_quaddec_synapse_action {
|
||||
FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
};
|
||||
|
||||
static enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
|
||||
[FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
|
||||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES
|
||||
};
|
||||
|
||||
enum ftm_quaddec_count_function {
|
||||
FTM_QUADDEC_COUNT_ENCODER_MODE_1,
|
||||
};
|
||||
|
||||
static const enum counter_count_function ftm_quaddec_count_functions[] = {
|
||||
[FTM_QUADDEC_COUNT_ENCODER_MODE_1] =
|
||||
COUNTER_COUNT_FUNCTION_QUADRATURE_X4
|
||||
};
|
||||
|
||||
static int ftm_quaddec_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_read_value *val)
|
||||
{
|
||||
struct ftm_quaddec *const ftm = counter->priv;
|
||||
uint32_t cntval;
|
||||
|
||||
ftm_read(ftm, FTM_CNT, &cntval);
|
||||
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cntval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_count_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_write_value *val)
|
||||
{
|
||||
struct ftm_quaddec *const ftm = counter->priv;
|
||||
u32 cnt;
|
||||
int err;
|
||||
|
||||
err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cnt != 0) {
|
||||
dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ftm_reset_counter(ftm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_count_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
{
|
||||
*function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
{
|
||||
*action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_ops ftm_quaddec_cnt_ops = {
|
||||
.count_read = ftm_quaddec_count_read,
|
||||
.count_write = ftm_quaddec_count_write,
|
||||
.function_get = ftm_quaddec_count_function_get,
|
||||
.action_get = ftm_quaddec_action_get,
|
||||
};
|
||||
|
||||
static struct counter_signal ftm_quaddec_signals[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.name = "Channel 1 Phase A"
|
||||
},
|
||||
{
|
||||
.id = 1,
|
||||
.name = "Channel 1 Phase B"
|
||||
}
|
||||
};
|
||||
|
||||
static struct counter_synapse ftm_quaddec_count_synapses[] = {
|
||||
{
|
||||
.actions_list = ftm_quaddec_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
|
||||
.signal = &ftm_quaddec_signals[0]
|
||||
},
|
||||
{
|
||||
.actions_list = ftm_quaddec_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
|
||||
.signal = &ftm_quaddec_signals[1]
|
||||
}
|
||||
};
|
||||
|
||||
static const struct counter_count_ext ftm_quaddec_count_ext[] = {
|
||||
COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
|
||||
COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
|
||||
};
|
||||
|
||||
static struct counter_count ftm_quaddec_counts = {
|
||||
.id = 0,
|
||||
.name = "Channel 1 Count",
|
||||
.functions_list = ftm_quaddec_count_functions,
|
||||
.num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
|
||||
.synapses = ftm_quaddec_count_synapses,
|
||||
.num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
|
||||
.ext = ftm_quaddec_count_ext,
|
||||
.num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
|
||||
};
|
||||
|
||||
static int ftm_quaddec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ftm_quaddec *ftm;
|
||||
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct resource *io;
|
||||
int ret;
|
||||
|
||||
ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL);
|
||||
if (!ftm)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ftm);
|
||||
|
||||
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!io) {
|
||||
dev_err(&pdev->dev, "Failed to get memory region\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ftm->pdev = pdev;
|
||||
ftm->big_endian = of_property_read_bool(node, "big-endian");
|
||||
ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
|
||||
|
||||
if (!ftm->ftm_base) {
|
||||
dev_err(&pdev->dev, "Failed to map memory region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ftm->counter.name = dev_name(&pdev->dev);
|
||||
ftm->counter.parent = &pdev->dev;
|
||||
ftm->counter.ops = &ftm_quaddec_cnt_ops;
|
||||
ftm->counter.counts = &ftm_quaddec_counts;
|
||||
ftm->counter.num_counts = 1;
|
||||
ftm->counter.signals = ftm_quaddec_signals;
|
||||
ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals);
|
||||
ftm->counter.priv = ftm;
|
||||
|
||||
mutex_init(&ftm->ftm_quaddec_mutex);
|
||||
|
||||
ftm_quaddec_init(ftm);
|
||||
|
||||
ret = counter_register(&ftm->counter);
|
||||
if (ret)
|
||||
ftm_quaddec_disable(ftm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ftm_quaddec *ftm = platform_get_drvdata(pdev);
|
||||
|
||||
counter_unregister(&ftm->counter);
|
||||
|
||||
ftm_quaddec_disable(ftm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ftm_quaddec_match[] = {
|
||||
{ .compatible = "fsl,ftm-quaddec" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver ftm_quaddec_driver = {
|
||||
.driver = {
|
||||
.name = "ftm-quaddec",
|
||||
.of_match_table = ftm_quaddec_match,
|
||||
},
|
||||
.probe = ftm_quaddec_probe,
|
||||
.remove = ftm_quaddec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ftm_quaddec_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com");
|
||||
MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com");
|
|
@ -0,0 +1,754 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* STM32 Low-Power Timer Encoder and Counter driver
|
||||
*
|
||||
* Copyright (C) STMicroelectronics 2017
|
||||
*
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>
|
||||
*
|
||||
* Inspired by 104-quad-8 and stm32-timer-trigger drivers.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/mfd/stm32-lptimer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct stm32_lptim_cnt {
|
||||
struct counter_device counter;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
u32 ceiling;
|
||||
u32 polarity;
|
||||
u32 quadrature_mode;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return FIELD_GET(STM32_LPTIM_ENABLE, val);
|
||||
}
|
||||
|
||||
static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
|
||||
int enable)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!enable) {
|
||||
clk_disable(priv->clk);
|
||||
priv->enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LP timer must be enabled before writing CMP & ARR */
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->ceiling);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ensure CMP & ARR registers are properly written */
|
||||
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
|
||||
(val & STM32_LPTIM_CMPOK_ARROK),
|
||||
100, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
|
||||
STM32_LPTIM_CMPOKCF_ARROKCF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret) {
|
||||
regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
|
||||
return ret;
|
||||
}
|
||||
priv->enabled = true;
|
||||
|
||||
/* Start LP timer in continuous mode */
|
||||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
|
||||
STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
|
||||
}
|
||||
|
||||
static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
|
||||
{
|
||||
u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
|
||||
STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
|
||||
u32 val;
|
||||
|
||||
/* Setup LP timer encoder/counter and polarity, without prescaler */
|
||||
if (priv->quadrature_mode)
|
||||
val = enable ? STM32_LPTIM_ENC : 0;
|
||||
else
|
||||
val = enable ? STM32_LPTIM_COUNTMODE : 0;
|
||||
val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
|
||||
|
||||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
|
||||
}
|
||||
|
||||
static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
if (val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check nobody uses the timer, or already disabled/enabled */
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if ((ret < 0) || (!ret && !val))
|
||||
return ret;
|
||||
if (val && ret)
|
||||
return -EBUSY;
|
||||
|
||||
ret = stm32_lptim_setup(priv, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return stm32_lptim_set_enable_state(priv, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
u32 dat;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = dat;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Non-quadrature mode: scale = 1 */
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
if (priv->quadrature_mode) {
|
||||
/*
|
||||
* Quadrature encoder mode:
|
||||
* - both edges, quarter cycle, scale is 0.25
|
||||
* - either rising/falling edge scale is 0.5
|
||||
*/
|
||||
if (priv->polarity > 1)
|
||||
*val2 = 2;
|
||||
else
|
||||
*val2 = 1;
|
||||
}
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info stm32_lptim_cnt_iio_info = {
|
||||
.read_raw = stm32_lptim_read_raw,
|
||||
.write_raw = stm32_lptim_write_raw,
|
||||
};
|
||||
|
||||
static const char *const stm32_lptim_quadrature_modes[] = {
|
||||
"non-quadrature",
|
||||
"quadrature",
|
||||
};
|
||||
|
||||
static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->quadrature_mode;
|
||||
}
|
||||
|
||||
static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
priv->quadrature_mode = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum stm32_lptim_quadrature_mode_en = {
|
||||
.items = stm32_lptim_quadrature_modes,
|
||||
.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
|
||||
.get = stm32_lptim_get_quadrature_mode,
|
||||
.set = stm32_lptim_set_quadrature_mode,
|
||||
};
|
||||
|
||||
static const char * const stm32_lptim_cnt_polarity[] = {
|
||||
"rising-edge", "falling-edge", "both-edges",
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->polarity;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
priv->polarity = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum stm32_lptim_cnt_polarity_en = {
|
||||
.items = stm32_lptim_cnt_polarity,
|
||||
.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
|
||||
.get = stm32_lptim_cnt_get_polarity,
|
||||
.set = stm32_lptim_cnt_set_polarity,
|
||||
};
|
||||
|
||||
static ssize_t stm32_lptim_cnt_get_ceiling(struct stm32_lptim_cnt *priv,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling);
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_set_ceiling(struct stm32_lptim_cnt *priv,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
ret = kstrtouint(buf, 0, &priv->ceiling);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->ceiling > STM32_LPTIM_MAX_ARR)
|
||||
return -EINVAL;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_get_preset_iio(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return stm32_lptim_cnt_get_ceiling(priv, buf);
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_set_preset_iio(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return stm32_lptim_cnt_set_ceiling(priv, buf, len);
|
||||
}
|
||||
|
||||
/* LP timer with encoder */
|
||||
static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
|
||||
{
|
||||
.name = "preset",
|
||||
.shared = IIO_SEPARATE,
|
||||
.read = stm32_lptim_cnt_get_preset_iio,
|
||||
.write = stm32_lptim_cnt_set_preset_iio,
|
||||
},
|
||||
IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
|
||||
IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
|
||||
IIO_ENUM("quadrature_mode", IIO_SEPARATE,
|
||||
&stm32_lptim_quadrature_mode_en),
|
||||
IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec stm32_lptim_enc_channels = {
|
||||
.type = IIO_COUNT,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_ENABLE) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = stm32_lptim_enc_ext_info,
|
||||
.indexed = 1,
|
||||
};
|
||||
|
||||
/* LP timer without encoder (counter only) */
|
||||
static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
|
||||
{
|
||||
.name = "preset",
|
||||
.shared = IIO_SEPARATE,
|
||||
.read = stm32_lptim_cnt_get_preset_iio,
|
||||
.write = stm32_lptim_cnt_set_preset_iio,
|
||||
},
|
||||
IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
|
||||
IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec stm32_lptim_cnt_channels = {
|
||||
.type = IIO_COUNT,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_ENABLE) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = stm32_lptim_cnt_ext_info,
|
||||
.indexed = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_lptim_cnt_function - enumerates stm32 LPTimer counter & encoder modes
|
||||
* @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
|
||||
* @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
|
||||
*/
|
||||
enum stm32_lptim_cnt_function {
|
||||
STM32_LPTIM_COUNTER_INCREASE,
|
||||
STM32_LPTIM_ENCODER_BOTH_EDGE,
|
||||
};
|
||||
|
||||
static enum counter_count_function stm32_lptim_cnt_functions[] = {
|
||||
[STM32_LPTIM_COUNTER_INCREASE] = COUNTER_COUNT_FUNCTION_INCREASE,
|
||||
[STM32_LPTIM_ENCODER_BOTH_EDGE] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
enum stm32_lptim_synapse_action {
|
||||
STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
|
||||
STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
STM32_LPTIM_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
static enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {
|
||||
/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_read_value *val)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &cnt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
if (!priv->quadrature_mode) {
|
||||
*function = STM32_LPTIM_COUNTER_INCREASE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
|
||||
*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_function_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t function)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
switch (function) {
|
||||
case STM32_LPTIM_COUNTER_INCREASE:
|
||||
priv->quadrature_mode = 0;
|
||||
return 0;
|
||||
case STM32_LPTIM_ENCODER_BOTH_EDGE:
|
||||
priv->quadrature_mode = 1;
|
||||
priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
int ret;
|
||||
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", ret);
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check nobody uses the timer, or already disabled/enabled */
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if ((ret < 0) || (!ret && !enable))
|
||||
return ret;
|
||||
if (enable && ret)
|
||||
return -EBUSY;
|
||||
|
||||
ret = stm32_lptim_setup(priv, enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm32_lptim_set_enable_state(priv, enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
return stm32_lptim_cnt_get_ceiling(priv, buf);
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
return stm32_lptim_cnt_set_ceiling(priv, buf, len);
|
||||
}
|
||||
|
||||
static const struct counter_count_ext stm32_lptim_cnt_ext[] = {
|
||||
{
|
||||
.name = "enable",
|
||||
.read = stm32_lptim_cnt_enable_read,
|
||||
.write = stm32_lptim_cnt_enable_write
|
||||
},
|
||||
{
|
||||
.name = "ceiling",
|
||||
.read = stm32_lptim_cnt_ceiling_read,
|
||||
.write = stm32_lptim_cnt_ceiling_write
|
||||
},
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
size_t function;
|
||||
int err;
|
||||
|
||||
err = stm32_lptim_cnt_function_get(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (function) {
|
||||
case STM32_LPTIM_COUNTER_INCREASE:
|
||||
/* LP Timer acts as up-counter on input 1 */
|
||||
if (synapse->signal->id == count->synapses[0].signal->id)
|
||||
*action = priv->polarity;
|
||||
else
|
||||
*action = STM32_LPTIM_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case STM32_LPTIM_ENCODER_BOTH_EDGE:
|
||||
*action = priv->polarity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_action_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t action)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
size_t function;
|
||||
int err;
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
err = stm32_lptim_cnt_function_get(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* only set polarity when in counter mode (on input 1) */
|
||||
if (function == STM32_LPTIM_COUNTER_INCREASE
|
||||
&& synapse->signal->id == count->synapses[0].signal->id) {
|
||||
switch (action) {
|
||||
case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
|
||||
case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
|
||||
case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
|
||||
priv->polarity = action;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct counter_ops stm32_lptim_cnt_ops = {
|
||||
.count_read = stm32_lptim_cnt_read,
|
||||
.function_get = stm32_lptim_cnt_function_get,
|
||||
.function_set = stm32_lptim_cnt_function_set,
|
||||
.action_get = stm32_lptim_cnt_action_get,
|
||||
.action_set = stm32_lptim_cnt_action_set,
|
||||
};
|
||||
|
||||
static struct counter_signal stm32_lptim_cnt_signals[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.name = "Channel 1 Quadrature A"
|
||||
},
|
||||
{
|
||||
.id = 1,
|
||||
.name = "Channel 1 Quadrature B"
|
||||
}
|
||||
};
|
||||
|
||||
static struct counter_synapse stm32_lptim_cnt_synapses[] = {
|
||||
{
|
||||
.actions_list = stm32_lptim_cnt_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_lptim_cnt_synapse_actions),
|
||||
.signal = &stm32_lptim_cnt_signals[0]
|
||||
},
|
||||
{
|
||||
.actions_list = stm32_lptim_cnt_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_lptim_cnt_synapse_actions),
|
||||
.signal = &stm32_lptim_cnt_signals[1]
|
||||
}
|
||||
};
|
||||
|
||||
/* LP timer with encoder */
|
||||
static struct counter_count stm32_lptim_enc_counts = {
|
||||
.id = 0,
|
||||
.name = "LPTimer Count",
|
||||
.functions_list = stm32_lptim_cnt_functions,
|
||||
.num_functions = ARRAY_SIZE(stm32_lptim_cnt_functions),
|
||||
.synapses = stm32_lptim_cnt_synapses,
|
||||
.num_synapses = ARRAY_SIZE(stm32_lptim_cnt_synapses),
|
||||
.ext = stm32_lptim_cnt_ext,
|
||||
.num_ext = ARRAY_SIZE(stm32_lptim_cnt_ext)
|
||||
};
|
||||
|
||||
/* LP timer without encoder (counter only) */
|
||||
static struct counter_count stm32_lptim_in1_counts = {
|
||||
.id = 0,
|
||||
.name = "LPTimer Count",
|
||||
.functions_list = stm32_lptim_cnt_functions,
|
||||
.num_functions = 1,
|
||||
.synapses = stm32_lptim_cnt_synapses,
|
||||
.num_synapses = 1,
|
||||
.ext = stm32_lptim_cnt_ext,
|
||||
.num_ext = ARRAY_SIZE(stm32_lptim_cnt_ext)
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct stm32_lptim_cnt *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
if (IS_ERR_OR_NULL(ddata))
|
||||
return -EINVAL;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->dev = &pdev->dev;
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->ceiling = STM32_LPTIM_MAX_ARR;
|
||||
|
||||
/* Initialize IIO device */
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &stm32_lptim_cnt_iio_info;
|
||||
if (ddata->has_encoder)
|
||||
indio_dev->channels = &stm32_lptim_enc_channels;
|
||||
else
|
||||
indio_dev->channels = &stm32_lptim_cnt_channels;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
/* Initialize Counter device */
|
||||
priv->counter.name = dev_name(&pdev->dev);
|
||||
priv->counter.parent = &pdev->dev;
|
||||
priv->counter.ops = &stm32_lptim_cnt_ops;
|
||||
if (ddata->has_encoder) {
|
||||
priv->counter.counts = &stm32_lptim_enc_counts;
|
||||
priv->counter.num_signals = ARRAY_SIZE(stm32_lptim_cnt_signals);
|
||||
} else {
|
||||
priv->counter.counts = &stm32_lptim_in1_counts;
|
||||
priv->counter.num_signals = 1;
|
||||
}
|
||||
priv->counter.num_counts = 1;
|
||||
priv->counter.signals = stm32_lptim_cnt_signals;
|
||||
priv->counter.priv = priv;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_counter_register(&pdev->dev, &priv->counter);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stm32_lptim_cnt_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Only take care of enabled counter: don't disturb other MFD child */
|
||||
if (priv->enabled) {
|
||||
ret = stm32_lptim_setup(priv, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm32_lptim_set_enable_state(priv, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Force enable state for later resume */
|
||||
priv->enabled = true;
|
||||
}
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->enabled) {
|
||||
priv->enabled = false;
|
||||
ret = stm32_lptim_setup(priv, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm32_lptim_set_enable_state(priv, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stm32_lptim_cnt_pm_ops, stm32_lptim_cnt_suspend,
|
||||
stm32_lptim_cnt_resume);
|
||||
|
||||
static const struct of_device_id stm32_lptim_cnt_of_match[] = {
|
||||
{ .compatible = "st,stm32-lptimer-counter", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
|
||||
|
||||
static struct platform_driver stm32_lptim_cnt_driver = {
|
||||
.probe = stm32_lptim_cnt_probe,
|
||||
.driver = {
|
||||
.name = "stm32-lptimer-counter",
|
||||
.of_match_table = stm32_lptim_cnt_of_match,
|
||||
.pm = &stm32_lptim_cnt_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_lptim_cnt_driver);
|
||||
|
||||
MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
|
||||
MODULE_ALIAS("platform:stm32-lptimer-counter");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,390 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* STM32 Timer Encoder and Counter driver
|
||||
*
|
||||
* Copyright (C) STMicroelectronics 2018
|
||||
*
|
||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/counter.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define TIM_CCMR_CCXS (BIT(8) | BIT(0))
|
||||
#define TIM_CCMR_MASK (TIM_CCMR_CC1S | TIM_CCMR_CC2S | \
|
||||
TIM_CCMR_IC1F | TIM_CCMR_IC2F)
|
||||
#define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \
|
||||
TIM_CCER_CC2P | TIM_CCER_CC2NP)
|
||||
|
||||
struct stm32_timer_cnt {
|
||||
struct counter_device counter;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
u32 ceiling;
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_count_function - enumerates stm32 timer counter encoder modes
|
||||
* @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
|
||||
* @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
|
||||
* @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
|
||||
* @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges
|
||||
*/
|
||||
enum stm32_count_function {
|
||||
STM32_COUNT_SLAVE_MODE_DISABLED = -1,
|
||||
STM32_COUNT_ENCODER_MODE_1,
|
||||
STM32_COUNT_ENCODER_MODE_2,
|
||||
STM32_COUNT_ENCODER_MODE_3,
|
||||
};
|
||||
|
||||
static enum counter_count_function stm32_count_functions[] = {
|
||||
[STM32_COUNT_ENCODER_MODE_1] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A,
|
||||
[STM32_COUNT_ENCODER_MODE_2] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B,
|
||||
[STM32_COUNT_ENCODER_MODE_3] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
static int stm32_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_read_value *val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CNT, &cnt);
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_write_value *val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
int err;
|
||||
|
||||
err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cnt > priv->ceiling)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(priv->regmap, TIM_CNT, cnt);
|
||||
}
|
||||
|
||||
static int stm32_count_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 smcr;
|
||||
|
||||
regmap_read(priv->regmap, TIM_SMCR, &smcr);
|
||||
|
||||
switch (smcr & TIM_SMCR_SMS) {
|
||||
case 1:
|
||||
*function = STM32_COUNT_ENCODER_MODE_1;
|
||||
return 0;
|
||||
case 2:
|
||||
*function = STM32_COUNT_ENCODER_MODE_2;
|
||||
return 0;
|
||||
case 3:
|
||||
*function = STM32_COUNT_ENCODER_MODE_3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_count_function_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t function)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cr1, sms;
|
||||
|
||||
switch (function) {
|
||||
case STM32_COUNT_ENCODER_MODE_1:
|
||||
sms = 1;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_2:
|
||||
sms = 2;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_3:
|
||||
sms = 3;
|
||||
break;
|
||||
default:
|
||||
sms = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Store enable status */
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
|
||||
/* TIMx_ARR register shouldn't be buffered (ARPE=0) */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
|
||||
regmap_write(priv->regmap, TIM_ARR, priv->ceiling);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms);
|
||||
|
||||
/* Make sure that registers are updated */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
|
||||
|
||||
/* Restore the enable status */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, cr1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_direction_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
const char *direction;
|
||||
u32 cr1;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward";
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", direction);
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 arr;
|
||||
|
||||
regmap_read(priv->regmap, TIM_ARR, &arr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", arr);
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
unsigned int ceiling;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 0, &ceiling);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* TIMx_ARR register shouldn't be buffered (ARPE=0) */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
|
||||
regmap_write(priv->regmap, TIM_ARR, ceiling);
|
||||
|
||||
priv->ceiling = ceiling;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cr1;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN));
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
int err;
|
||||
u32 cr1;
|
||||
bool enable;
|
||||
|
||||
err = kstrtobool(buf, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (enable) {
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
if (!(cr1 & TIM_CR1_CEN))
|
||||
clk_enable(priv->clk);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
|
||||
TIM_CR1_CEN);
|
||||
} else {
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
if (cr1 & TIM_CR1_CEN)
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct counter_count_ext stm32_count_ext[] = {
|
||||
{
|
||||
.name = "direction",
|
||||
.read = stm32_count_direction_read,
|
||||
},
|
||||
{
|
||||
.name = "enable",
|
||||
.read = stm32_count_enable_read,
|
||||
.write = stm32_count_enable_write
|
||||
},
|
||||
{
|
||||
.name = "ceiling",
|
||||
.read = stm32_count_ceiling_read,
|
||||
.write = stm32_count_ceiling_write
|
||||
},
|
||||
};
|
||||
|
||||
enum stm32_synapse_action {
|
||||
STM32_SYNAPSE_ACTION_NONE,
|
||||
STM32_SYNAPSE_ACTION_BOTH_EDGES
|
||||
};
|
||||
|
||||
static enum counter_synapse_action stm32_synapse_actions[] = {
|
||||
[STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
[STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
|
||||
};
|
||||
|
||||
static int stm32_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
{
|
||||
size_t function;
|
||||
int err;
|
||||
|
||||
/* Default action mode (e.g. STM32_COUNT_SLAVE_MODE_DISABLED) */
|
||||
*action = STM32_SYNAPSE_ACTION_NONE;
|
||||
|
||||
err = stm32_count_function_get(counter, count, &function);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
switch (function) {
|
||||
case STM32_COUNT_ENCODER_MODE_1:
|
||||
/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
|
||||
if (synapse->signal->id == count->synapses[0].signal->id)
|
||||
*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_2:
|
||||
/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
|
||||
if (synapse->signal->id == count->synapses[1].signal->id)
|
||||
*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_3:
|
||||
/* counts up/down on both TI1FP1 and TI2FP2 edges */
|
||||
*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_ops stm32_timer_cnt_ops = {
|
||||
.count_read = stm32_count_read,
|
||||
.count_write = stm32_count_write,
|
||||
.function_get = stm32_count_function_get,
|
||||
.function_set = stm32_count_function_set,
|
||||
.action_get = stm32_action_get,
|
||||
};
|
||||
|
||||
static struct counter_signal stm32_signals[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.name = "Channel 1 Quadrature A"
|
||||
},
|
||||
{
|
||||
.id = 1,
|
||||
.name = "Channel 1 Quadrature B"
|
||||
}
|
||||
};
|
||||
|
||||
static struct counter_synapse stm32_count_synapses[] = {
|
||||
{
|
||||
.actions_list = stm32_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
|
||||
.signal = &stm32_signals[0]
|
||||
},
|
||||
{
|
||||
.actions_list = stm32_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
|
||||
.signal = &stm32_signals[1]
|
||||
}
|
||||
};
|
||||
|
||||
static struct counter_count stm32_counts = {
|
||||
.id = 0,
|
||||
.name = "Channel 1 Count",
|
||||
.functions_list = stm32_count_functions,
|
||||
.num_functions = ARRAY_SIZE(stm32_count_functions),
|
||||
.synapses = stm32_count_synapses,
|
||||
.num_synapses = ARRAY_SIZE(stm32_count_synapses),
|
||||
.ext = stm32_count_ext,
|
||||
.num_ext = ARRAY_SIZE(stm32_count_ext)
|
||||
};
|
||||
|
||||
static int stm32_timer_cnt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_timer_cnt *priv;
|
||||
|
||||
if (IS_ERR_OR_NULL(ddata))
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->ceiling = ddata->max_arr;
|
||||
|
||||
priv->counter.name = dev_name(dev);
|
||||
priv->counter.parent = dev;
|
||||
priv->counter.ops = &stm32_timer_cnt_ops;
|
||||
priv->counter.counts = &stm32_counts;
|
||||
priv->counter.num_counts = 1;
|
||||
priv->counter.signals = stm32_signals;
|
||||
priv->counter.num_signals = ARRAY_SIZE(stm32_signals);
|
||||
priv->counter.priv = priv;
|
||||
|
||||
/* Register Counter device */
|
||||
return devm_counter_register(dev, &priv->counter);
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_timer_cnt_of_match[] = {
|
||||
{ .compatible = "st,stm32-timer-counter", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_timer_cnt_of_match);
|
||||
|
||||
static struct platform_driver stm32_timer_cnt_driver = {
|
||||
.probe = stm32_timer_cnt_probe,
|
||||
.driver = {
|
||||
.name = "stm32-timer-counter",
|
||||
.of_match_table = stm32_timer_cnt_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_timer_cnt_driver);
|
||||
|
||||
MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
|
||||
MODULE_ALIAS("platform:stm32-timer-counter");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 TIMER counter driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -38,28 +38,28 @@ config IIO_TRIGGER
|
|||
data now' interrupt.
|
||||
|
||||
config IIO_CONSUMERS_PER_TRIGGER
|
||||
int "Maximum number of consumers per trigger"
|
||||
depends on IIO_TRIGGER
|
||||
default "2"
|
||||
help
|
||||
This value controls the maximum number of consumers that a
|
||||
given trigger may handle. Default is 2.
|
||||
int "Maximum number of consumers per trigger"
|
||||
depends on IIO_TRIGGER
|
||||
default "2"
|
||||
help
|
||||
This value controls the maximum number of consumers that a
|
||||
given trigger may handle. Default is 2.
|
||||
|
||||
config IIO_SW_DEVICE
|
||||
tristate "Enable software IIO device support"
|
||||
select IIO_CONFIGFS
|
||||
help
|
||||
Provides IIO core support for software devices. A software
|
||||
device can be created via configfs or directly by a driver
|
||||
using the API provided.
|
||||
Provides IIO core support for software devices. A software
|
||||
device can be created via configfs or directly by a driver
|
||||
using the API provided.
|
||||
|
||||
config IIO_SW_TRIGGER
|
||||
tristate "Enable software triggers support"
|
||||
select IIO_CONFIGFS
|
||||
help
|
||||
Provides IIO core support for software triggers. A software
|
||||
trigger can be created via configfs or directly by a driver
|
||||
using the API provided.
|
||||
Provides IIO core support for software triggers. A software
|
||||
trigger can be created via configfs or directly by a driver
|
||||
using the API provided.
|
||||
|
||||
config IIO_TRIGGERED_EVENT
|
||||
tristate
|
||||
|
@ -73,7 +73,6 @@ source "drivers/iio/afe/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"
|
||||
|
|
|
@ -20,7 +20,6 @@ obj-y += amplifiers/
|
|||
obj-y += buffer/
|
||||
obj-y += chemical/
|
||||
obj-y += common/
|
||||
obj-y += counter/
|
||||
obj-y += dac/
|
||||
obj-y += dummy/
|
||||
obj-y += gyro/
|
||||
|
|
|
@ -6,28 +6,28 @@
|
|||
menu "Accelerometers"
|
||||
|
||||
config ADIS16201
|
||||
tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say Y here to build support for Analog Devices adis16201 dual-axis
|
||||
digital inclinometer and accelerometer.
|
||||
tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say Y here to build support for Analog Devices adis16201 dual-axis
|
||||
digital inclinometer and accelerometer.
|
||||
|
||||
To compile this driver as a module, say M here: the module will
|
||||
be called adis16201.
|
||||
To compile this driver as a module, say M here: the module will
|
||||
be called adis16201.
|
||||
|
||||
config ADIS16209
|
||||
tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
|
||||
and accelerometer.
|
||||
tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
|
||||
and accelerometer.
|
||||
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called adis16209.
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called adis16209.
|
||||
|
||||
config ADXL345
|
||||
tristate
|
||||
|
@ -100,16 +100,16 @@ config BMA180
|
|||
module will be called bma180.
|
||||
|
||||
config BMA220
|
||||
tristate "Bosch BMA220 3-Axis Accelerometer Driver"
|
||||
tristate "Bosch BMA220 3-Axis Accelerometer Driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to add support for the Bosch BMA220 triaxial
|
||||
acceleration sensor.
|
||||
help
|
||||
Say yes here to add support for the Bosch BMA220 triaxial
|
||||
acceleration sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma220_spi.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma220_spi.
|
||||
|
||||
config BMC150_ACCEL
|
||||
tristate "Bosch BMC150 Accelerometer Driver"
|
||||
|
@ -223,7 +223,7 @@ config IIO_ST_ACCEL_3AXIS
|
|||
Say yes here to build support for STMicroelectronics accelerometers:
|
||||
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
|
||||
LNG2DM, LIS3DE
|
||||
LNG2DM, LIS3DE, LIS2DE12
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
|
|
@ -116,6 +116,7 @@ struct bma180_data {
|
|||
struct i2c_client *client;
|
||||
struct iio_trigger *trig;
|
||||
const struct bma180_part_info *part_info;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct mutex mutex;
|
||||
bool sleep_state;
|
||||
int scale;
|
||||
|
@ -561,6 +562,15 @@ static int bma180_set_power_mode(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
bma180_accel_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
|
||||
return &data->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_enum bma180_power_mode_enum = {
|
||||
.items = bma180_power_modes,
|
||||
.num_items = ARRAY_SIZE(bma180_power_modes),
|
||||
|
@ -571,7 +581,8 @@ static const struct iio_enum bma180_power_mode_enum = {
|
|||
static const struct iio_chan_spec_ext_info bma180_ext_info[] = {
|
||||
IIO_ENUM("power_mode", true, &bma180_power_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum),
|
||||
{ },
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
#define BMA180_ACC_CHANNEL(_axis, _bits) { \
|
||||
|
@ -722,6 +733,11 @@ static int bma180_probe(struct i2c_client *client,
|
|||
chip = id->driver_data;
|
||||
data->part_info = &bma180_part_info[chip];
|
||||
|
||||
ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = data->part_info->chip_config(data);
|
||||
if (ret < 0)
|
||||
goto err_chip_disable;
|
||||
|
|
|
@ -204,6 +204,7 @@ struct bmc150_accel_data {
|
|||
int ev_enable_state;
|
||||
int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
|
||||
const struct bmc150_accel_chip_info *chip_info;
|
||||
struct iio_mount_matrix orientation;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
|
@ -393,7 +394,7 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
|
|||
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Failed: bmc150_accel_set_power_state for %d\n", on);
|
||||
"Failed: %s for %d\n", __func__, on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
|
@ -796,6 +797,20 @@ static ssize_t bmc150_accel_get_fifo_state(struct device *dev,
|
|||
return sprintf(buf, "%d\n", state);
|
||||
}
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
bmc150_accel_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
|
||||
return &data->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info bmc150_accel_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bmc150_accel_get_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
|
||||
static IIO_CONST_ATTR(hwfifo_watermark_max,
|
||||
__stringify(BMC150_ACCEL_FIFO_LENGTH));
|
||||
|
@ -978,6 +993,7 @@ static const struct iio_event_spec bmc150_accel_event = {
|
|||
.shift = 16 - (bits), \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
.ext_info = bmc150_accel_ext_info, \
|
||||
.event_spec = &bmc150_accel_event, \
|
||||
.num_event_specs = 1 \
|
||||
}
|
||||
|
@ -1555,6 +1571,11 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bmc150_accel_chip_init(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for older Chrome OS EC accelerometer
|
||||
*
|
||||
* Copyright 2017 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This driver uses the memory mapper cros-ec interface to communicate
|
||||
* with the Chrome OS EC about accelerometer data.
|
||||
* Accelerometer access is presented through iio sysfs.
|
||||
|
@ -29,7 +21,6 @@
|
|||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define DRV_NAME "cros-ec-accel-legacy"
|
||||
|
@ -353,7 +344,7 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
|
|||
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_accel_legacy_state *state;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
if (!ec || !ec->ec_dev) {
|
||||
dev_warn(&pdev->dev, "No EC device found.\n");
|
||||
|
@ -381,20 +372,17 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
|
|||
* Present the channel using HTML5 standard:
|
||||
* need to invert X and Y and invert some lid axis.
|
||||
*/
|
||||
for (i = X ; i < MAX_AXIS; i++) {
|
||||
switch (i) {
|
||||
case X:
|
||||
ec_accel_channels[X].scan_index = Y;
|
||||
case Y:
|
||||
ec_accel_channels[Y].scan_index = X;
|
||||
case Z:
|
||||
ec_accel_channels[Z].scan_index = Z;
|
||||
}
|
||||
if (state->sensor_num == MOTIONSENSE_LOC_LID && i != Y)
|
||||
state->sign[i] = -1;
|
||||
else
|
||||
state->sign[i] = 1;
|
||||
}
|
||||
ec_accel_channels[X].scan_index = Y;
|
||||
ec_accel_channels[Y].scan_index = X;
|
||||
ec_accel_channels[Z].scan_index = Z;
|
||||
|
||||
state->sign[Y] = 1;
|
||||
|
||||
if (state->sensor_num == MOTIONSENSE_LOC_LID)
|
||||
state->sign[X] = state->sign[Z] = -1;
|
||||
else
|
||||
state->sign[X] = state->sign[Z] = 1;
|
||||
|
||||
indio_dev->num_channels = ARRAY_SIZE(ec_accel_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &cros_ec_accel_legacy_info;
|
||||
|
@ -419,5 +407,5 @@ module_platform_driver(cros_ec_accel_platform_driver);
|
|||
|
||||
MODULE_DESCRIPTION("ChromeOS EC legacy accelerometer driver");
|
||||
MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
|
|
|
@ -451,7 +451,7 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
|
|||
}
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"Failed: kxcjk1013_set_power_state for %d\n", on);
|
||||
"Failed: %s for %d\n", __func__, on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&data->client->dev);
|
||||
return ret;
|
||||
|
@ -1491,6 +1491,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
|
|||
{"KXCJ1013", KXCJK1013},
|
||||
{"KXCJ1008", KXCJ91008},
|
||||
{"KXCJ9000", KXCJ91008},
|
||||
{"KIOX0008", KXCJ91008},
|
||||
{"KIOX0009", KXTJ21009},
|
||||
{"KIOX000A", KXCJ91008},
|
||||
{"KIOX010A", KXCJ91008}, /* KXCJ91008 inside the display of a 2-in-1 */
|
||||
|
@ -1512,10 +1513,20 @@ static const struct i2c_device_id kxcjk1013_id[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(i2c, kxcjk1013_id);
|
||||
|
||||
static const struct of_device_id kxcjk1013_of_match[] = {
|
||||
{ .compatible = "kionix,kxcjk1013", },
|
||||
{ .compatible = "kionix,kxcj91008", },
|
||||
{ .compatible = "kionix,kxtj21009", },
|
||||
{ .compatible = "kionix,kxtf9", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, kxcjk1013_of_match);
|
||||
|
||||
static struct i2c_driver kxcjk1013_driver = {
|
||||
.driver = {
|
||||
.name = KXCJK1013_DRV_NAME,
|
||||
.acpi_match_table = ACPI_PTR(kx_acpi_match),
|
||||
.of_match_table = kxcjk1013_of_match,
|
||||
.pm = &kxcjk1013_pm_ops,
|
||||
},
|
||||
.probe = kxcjk1013_probe,
|
||||
|
|
|
@ -420,9 +420,7 @@ int kxsd9_common_probe(struct device *dev,
|
|||
indio_dev->available_scan_masks = kxsd9_scan_masks;
|
||||
|
||||
/* Read the mounting matrix, if present */
|
||||
ret = of_iio_read_mount_matrix(dev,
|
||||
"mount-matrix",
|
||||
&st->orientation);
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -1580,7 +1580,7 @@ static int mma8452_probe(struct i2c_client *client,
|
|||
case FXLS8471_DEVICE_ID:
|
||||
if (ret == data->chip_info->chip_id)
|
||||
break;
|
||||
/* else: fall through */
|
||||
/* fall through */
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
goto disable_regulators;
|
||||
|
|
|
@ -34,6 +34,7 @@ enum st_accel_type {
|
|||
LIS3LV02DL,
|
||||
LIS2DW12,
|
||||
LIS3DHH,
|
||||
LIS2DE12,
|
||||
ST_ACCEL_MAX,
|
||||
};
|
||||
|
||||
|
@ -57,6 +58,7 @@ enum st_accel_type {
|
|||
#define LIS2DW12_ACCEL_DEV_NAME "lis2dw12"
|
||||
#define LIS3DHH_ACCEL_DEV_NAME "lis3dhh"
|
||||
#define LIS3DE_ACCEL_DEV_NAME "lis3de"
|
||||
#define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default accel platform data
|
||||
|
|
|
@ -831,6 +831,82 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.multi_read_bit = false,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = 0x33,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = LIS2DE12_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
|
||||
.odr = {
|
||||
.addr = 0x20,
|
||||
.mask = 0xf0,
|
||||
.odr_avl = {
|
||||
{ .hz = 1, .value = 0x01, },
|
||||
{ .hz = 10, .value = 0x02, },
|
||||
{ .hz = 25, .value = 0x03, },
|
||||
{ .hz = 50, .value = 0x04, },
|
||||
{ .hz = 100, .value = 0x05, },
|
||||
{ .hz = 200, .value = 0x06, },
|
||||
{ .hz = 400, .value = 0x07, },
|
||||
{ .hz = 1620, .value = 0x08, },
|
||||
{ .hz = 5376, .value = 0x09, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = 0x20,
|
||||
.mask = 0xf0,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = 0x23,
|
||||
.mask = 0x30,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = 0x00,
|
||||
.gain = IIO_G_TO_M_S_2(15600),
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = 0x01,
|
||||
.gain = IIO_G_TO_M_S_2(31200),
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = 0x02,
|
||||
.gain = IIO_G_TO_M_S_2(62500),
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_ACCEL_FS_AVL_16G,
|
||||
.value = 0x03,
|
||||
.gain = IIO_G_TO_M_S_2(187500),
|
||||
},
|
||||
},
|
||||
},
|
||||
.drdy_irq = {
|
||||
.int1 = {
|
||||
.addr = 0x22,
|
||||
.mask = 0x10,
|
||||
},
|
||||
.addr_ihl = 0x25,
|
||||
.mask_ihl = 0x02,
|
||||
.stat_drdy = {
|
||||
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
.mask = 0x07,
|
||||
},
|
||||
},
|
||||
.sim = {
|
||||
.addr = 0x23,
|
||||
.value = BIT(0),
|
||||
},
|
||||
.multi_read_bit = true,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -992,7 +1068,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev,
|
|||
goto out;
|
||||
|
||||
val = elements[i].integer.value;
|
||||
if (val < 0 || val > 2)
|
||||
if (val > 2)
|
||||
goto out;
|
||||
|
||||
/* Avoiding full matrix multiplication, we simply reorder the
|
||||
|
|
|
@ -102,6 +102,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
|||
.compatible = "st,lis3de",
|
||||
.data = LIS3DE_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis2de12",
|
||||
.data = LIS2DE12_ACCEL_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
|
@ -140,6 +144,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
|
|||
{ LIS3LV02DL_ACCEL_DEV_NAME },
|
||||
{ LIS2DW12_ACCEL_DEV_NAME },
|
||||
{ LIS3DE_ACCEL_DEV_NAME },
|
||||
{ LIS2DE12_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
|
|
@ -124,6 +124,18 @@ config AD7768_1
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7768-1.
|
||||
|
||||
config AD7780
|
||||
tristate "Analog Devices AD7780 and similar ADCs driver"
|
||||
depends on SPI
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7170, AD7171,
|
||||
AD7780 and AD7781 SPI analog to digital converters (ADC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7780.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
|
@ -390,7 +402,7 @@ config HX711
|
|||
|
||||
This driver uses two GPIOs, one acts as the clock and controls the
|
||||
channel selection and gain, the other one is used for the measurement
|
||||
data
|
||||
data
|
||||
|
||||
Currently the raw value is read from the chip and delivered.
|
||||
To get an actual weight one needs to subtract the
|
||||
|
@ -541,7 +553,7 @@ config MAX1363
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called max1363.
|
||||
|
||||
config MAX9611
|
||||
config MAX9611
|
||||
tristate "Maxim max9611/max9612 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
|
@ -585,17 +597,17 @@ config MCP3911
|
|||
called mcp3911.
|
||||
|
||||
config MEDIATEK_MT6577_AUXADC
|
||||
tristate "MediaTek AUXADC driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to enable support for MediaTek mt65xx AUXADC.
|
||||
tristate "MediaTek AUXADC driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to enable support for MediaTek mt65xx AUXADC.
|
||||
|
||||
The driver supports immediate mode operation to read from one of sixteen
|
||||
channels (external or internal).
|
||||
The driver supports immediate mode operation to read from one of sixteen
|
||||
channels (external or internal).
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mt6577_auxadc.
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mt6577_auxadc.
|
||||
|
||||
config MEN_Z188_ADC
|
||||
tristate "MEN 16z188 ADC IP Core support"
|
||||
|
@ -809,7 +821,9 @@ config STM32_DFSDM_ADC
|
|||
depends on (ARCH_STM32 && OF) || COMPILE_TEST
|
||||
select STM32_DFSDM_CORE
|
||||
select REGMAP_MMIO
|
||||
select IIO_BUFFER
|
||||
select IIO_BUFFER_HW_CONSUMER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Select this option to support ADCSigma delta modulator for
|
||||
STMicroelectronics STM32 digital filter for sigma delta converter.
|
||||
|
@ -956,7 +970,7 @@ config TI_ADS1015
|
|||
|
||||
config TI_ADS7950
|
||||
tristate "Texas Instruments ADS7950 ADC driver"
|
||||
depends on SPI
|
||||
depends on SPI && GPIOLIB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
|
@ -967,6 +981,16 @@ config TI_ADS7950
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ti-ads7950.
|
||||
|
||||
config TI_ADS8344
|
||||
tristate "Texas Instruments ADS8344"
|
||||
depends on SPI && OF
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADS8344
|
||||
ADC chips
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads8344.
|
||||
|
||||
config TI_ADS8688
|
||||
tristate "Texas Instruments ADS8688"
|
||||
depends on SPI && OF
|
||||
|
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
|
|||
obj-$(CONFIG_AD7606) += ad7606.o
|
||||
obj-$(CONFIG_AD7766) += ad7766.o
|
||||
obj-$(CONFIG_AD7768_1) += ad7768-1.o
|
||||
obj-$(CONFIG_AD7780) += ad7780.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
|
@ -87,6 +88,7 @@ obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
|||
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
|
|
|
@ -411,7 +411,7 @@ static int ad7124_init_channel_vref(struct ad7124_state *st,
|
|||
dev_err(&st->sd.spi->dev,
|
||||
"Error, trying to use external voltage reference without a %s regulator.\n",
|
||||
ad7124_ref_names[refsel]);
|
||||
return PTR_ERR(st->vref[refsel]);
|
||||
return PTR_ERR(st->vref[refsel]);
|
||||
}
|
||||
st->channel_config[channel_number].vref_mv =
|
||||
regulator_get_voltage(st->vref[refsel]);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
* Scales are computed as 5000/32768 and 10000/32768 respectively,
|
||||
* so that when applied to the raw values they provide mV values
|
||||
*/
|
||||
static const unsigned int scale_avail[2] = {
|
||||
static const unsigned int ad7606_scale_avail[2] = {
|
||||
152588, 305176
|
||||
};
|
||||
|
||||
|
@ -39,6 +39,10 @@ static const unsigned int ad7606_oversampling_avail[7] = {
|
|||
1, 2, 4, 8, 16, 32, 64,
|
||||
};
|
||||
|
||||
static const unsigned int ad7616_oversampling_avail[8] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128,
|
||||
};
|
||||
|
||||
static int ad7606_reset(struct ad7606_state *st)
|
||||
{
|
||||
if (st->gpio_reset) {
|
||||
|
@ -154,7 +158,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
|||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = scale_avail[st->range];
|
||||
*val2 = st->scale_avail[st->range];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*val = st->oversampling;
|
||||
|
@ -163,19 +167,29 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals,
|
||||
unsigned int n, bool micros)
|
||||
{
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
micros ? "0.%06u " : "%u ", vals[i]);
|
||||
}
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t in_voltage_scale_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
scale_avail[i]);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
|
||||
|
@ -193,7 +207,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
mutex_lock(&st->lock);
|
||||
i = find_closest(val2, scale_avail, ARRAY_SIZE(scale_avail));
|
||||
i = find_closest(val2, st->scale_avail, st->num_scales);
|
||||
gpiod_set_value(st->gpio_range, i);
|
||||
st->range = i;
|
||||
mutex_unlock(&st->lock);
|
||||
|
@ -202,15 +216,20 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
i = find_closest(val, ad7606_oversampling_avail,
|
||||
ARRAY_SIZE(ad7606_oversampling_avail));
|
||||
i = find_closest(val, st->oversampling_avail,
|
||||
st->num_os_ratios);
|
||||
|
||||
values[0] = i;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
|
||||
st->gpio_os->info, values);
|
||||
st->oversampling = ad7606_oversampling_avail[i];
|
||||
|
||||
/* AD7616 requires a reset to update value */
|
||||
if (st->chip_info->os_req_reset)
|
||||
ad7606_reset(st);
|
||||
|
||||
st->oversampling = st->oversampling_avail[i];
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return 0;
|
||||
|
@ -219,11 +238,23 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64");
|
||||
static ssize_t ad7606_oversampling_ratio_avail(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 ad7606_show_avail(buf, st->oversampling_avail,
|
||||
st->num_os_ratios, false);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444,
|
||||
ad7606_oversampling_ratio_avail, NULL, 0);
|
||||
|
||||
static struct attribute *ad7606_attributes_os_and_range[] = {
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
&iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -232,7 +263,7 @@ static const struct attribute_group ad7606_attribute_group_os_and_range = {
|
|||
};
|
||||
|
||||
static struct attribute *ad7606_attributes_os[] = {
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
&iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -292,6 +323,36 @@ static const struct iio_chan_spec ad7606_channels[] = {
|
|||
AD7606_CHANNEL(7),
|
||||
};
|
||||
|
||||
/*
|
||||
* The current assumption that this driver makes for AD7616, is that it's
|
||||
* working in Hardware Mode with Serial, Burst and Sequencer modes activated.
|
||||
* To activate them, following pins must be pulled high:
|
||||
* -SER/PAR
|
||||
* -SEQEN
|
||||
* And following pins must be pulled low:
|
||||
* -WR/BURST
|
||||
* -DB4/SER1W
|
||||
*/
|
||||
static const struct iio_chan_spec ad7616_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16),
|
||||
AD7606_CHANNEL(0),
|
||||
AD7606_CHANNEL(1),
|
||||
AD7606_CHANNEL(2),
|
||||
AD7606_CHANNEL(3),
|
||||
AD7606_CHANNEL(4),
|
||||
AD7606_CHANNEL(5),
|
||||
AD7606_CHANNEL(6),
|
||||
AD7606_CHANNEL(7),
|
||||
AD7606_CHANNEL(8),
|
||||
AD7606_CHANNEL(9),
|
||||
AD7606_CHANNEL(10),
|
||||
AD7606_CHANNEL(11),
|
||||
AD7606_CHANNEL(12),
|
||||
AD7606_CHANNEL(13),
|
||||
AD7606_CHANNEL(14),
|
||||
AD7606_CHANNEL(15),
|
||||
};
|
||||
|
||||
static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
|
||||
/* More devices added in future */
|
||||
[ID_AD7605_4] = {
|
||||
|
@ -301,17 +362,27 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
|
|||
[ID_AD7606_8] = {
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 9,
|
||||
.has_oversampling = true,
|
||||
.oversampling_avail = ad7606_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
|
||||
},
|
||||
[ID_AD7606_6] = {
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 7,
|
||||
.has_oversampling = true,
|
||||
.oversampling_avail = ad7606_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
|
||||
},
|
||||
[ID_AD7606_4] = {
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 5,
|
||||
.has_oversampling = true,
|
||||
.oversampling_avail = ad7606_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
|
||||
},
|
||||
[ID_AD7616] = {
|
||||
.channels = ad7616_channels,
|
||||
.num_channels = 17,
|
||||
.oversampling_avail = ad7616_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
|
||||
.os_req_reset = true,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -343,7 +414,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
|
|||
if (IS_ERR(st->gpio_frstdata))
|
||||
return PTR_ERR(st->gpio_frstdata);
|
||||
|
||||
if (!st->chip_info->has_oversampling)
|
||||
if (!st->chip_info->oversampling_num)
|
||||
return 0;
|
||||
|
||||
st->gpio_os = devm_gpiod_get_array_optional(dev,
|
||||
|
@ -467,6 +538,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
|||
/* tied to logic low, analog input range is +/- 5V */
|
||||
st->range = 0;
|
||||
st->oversampling = 1;
|
||||
st->scale_avail = ad7606_scale_avail;
|
||||
st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
|
||||
|
||||
st->reg = devm_regulator_get(dev, "avcc");
|
||||
if (IS_ERR(st->reg))
|
||||
|
@ -484,6 +557,11 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
|||
|
||||
st->chip_info = &ad7606_chip_info_tbl[id];
|
||||
|
||||
if (st->chip_info->oversampling_num) {
|
||||
st->oversampling_avail = st->chip_info->oversampling_avail;
|
||||
st->num_os_ratios = st->chip_info->oversampling_num;
|
||||
}
|
||||
|
||||
ret = ad7606_request_gpios(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -12,12 +12,17 @@
|
|||
* struct ad7606_chip_info - chip specific information
|
||||
* @channels: channel specification
|
||||
* @num_channels: number of channels
|
||||
* @has_oversampling: whether the device has oversampling support
|
||||
* @oversampling_avail pointer to the array which stores the available
|
||||
* oversampling ratios.
|
||||
* @oversampling_num number of elements stored in oversampling_avail array
|
||||
* @os_req_reset some devices require a reset to update oversampling
|
||||
*/
|
||||
struct ad7606_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
bool has_oversampling;
|
||||
const unsigned int *oversampling_avail;
|
||||
unsigned int oversampling_num;
|
||||
bool os_req_reset;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -29,6 +34,11 @@ struct ad7606_chip_info {
|
|||
* @range voltage range selection, selects which scale to apply
|
||||
* @oversampling oversampling selection
|
||||
* @base_address address from where to read data in parallel operation
|
||||
* @scale_avail pointer to the array which stores the available scales
|
||||
* @num_scales number of elements stored in the scale_avail array
|
||||
* @oversampling_avail pointer to the array which stores the available
|
||||
* oversampling ratios.
|
||||
* @num_os_ratios number of elements stored in oversampling_avail array
|
||||
* @lock protect sensor state from concurrent accesses to GPIOs
|
||||
* @gpio_convst GPIO descriptor for conversion start signal (CONVST)
|
||||
* @gpio_reset GPIO descriptor for device hard-reset
|
||||
|
@ -50,6 +60,10 @@ struct ad7606_state {
|
|||
unsigned int range;
|
||||
unsigned int oversampling;
|
||||
void __iomem *base_address;
|
||||
const unsigned int *scale_avail;
|
||||
unsigned int num_scales;
|
||||
const unsigned int *oversampling_avail;
|
||||
unsigned int num_os_ratios;
|
||||
|
||||
struct mutex lock; /* protect sensor state */
|
||||
struct gpio_desc *gpio_convst;
|
||||
|
@ -64,9 +78,9 @@ struct ad7606_state {
|
|||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* 8 * 16-bit samples + 64-bit timestamp
|
||||
* 16 * 16-bit samples + 64-bit timestamp
|
||||
*/
|
||||
unsigned short data[12] ____cacheline_aligned;
|
||||
unsigned short data[20] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -86,7 +100,8 @@ enum ad7606_supported_device_ids {
|
|||
ID_AD7605_4,
|
||||
ID_AD7606_8,
|
||||
ID_AD7606_6,
|
||||
ID_AD7606_4
|
||||
ID_AD7606_4,
|
||||
ID_AD7616,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
|
@ -53,6 +53,7 @@ static const struct spi_device_id ad7606_id_table[] = {
|
|||
{ "ad7606-4", ID_AD7606_4 },
|
||||
{ "ad7606-6", ID_AD7606_6 },
|
||||
{ "ad7606-8", ID_AD7606_8 },
|
||||
{ "ad7616", ID_AD7616 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
|
||||
|
@ -62,6 +63,7 @@ static const struct of_device_id ad7606_of_match[] = {
|
|||
{ .compatible = "adi,ad7606-4" },
|
||||
{ .compatible = "adi,ad7606-6" },
|
||||
{ .compatible = "adi,ad7606-8" },
|
||||
{ .compatible = "adi,ad7616" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7606_of_match);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7170/AD7171 and AD7780/AD7781 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
* Copyright 2019 Renato Lui Geh
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -17,6 +17,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
@ -28,16 +29,25 @@
|
|||
#define AD7780_ID1 BIT(4)
|
||||
#define AD7780_ID0 BIT(3)
|
||||
#define AD7780_GAIN BIT(2)
|
||||
#define AD7780_PAT1 BIT(1)
|
||||
#define AD7780_PAT0 BIT(0)
|
||||
|
||||
#define AD7780_PATTERN (AD7780_PAT0)
|
||||
#define AD7780_PATTERN_MASK (AD7780_PAT0 | AD7780_PAT1)
|
||||
#define AD7170_ID 0
|
||||
#define AD7171_ID 1
|
||||
#define AD7780_ID 1
|
||||
#define AD7781_ID 0
|
||||
|
||||
#define AD7170_PAT2 BIT(2)
|
||||
#define AD7780_ID_MASK (AD7780_ID0 | AD7780_ID1)
|
||||
|
||||
#define AD7170_PATTERN (AD7780_PAT0 | AD7170_PAT2)
|
||||
#define AD7170_PATTERN_MASK (AD7780_PAT0 | AD7780_PAT1 | AD7170_PAT2)
|
||||
#define AD7780_PATTERN_GOOD 1
|
||||
#define AD7780_PATTERN_MASK GENMASK(1, 0)
|
||||
|
||||
#define AD7170_PATTERN_GOOD 5
|
||||
#define AD7170_PATTERN_MASK GENMASK(2, 0)
|
||||
|
||||
#define AD7780_GAIN_MIDPOINT 64
|
||||
#define AD7780_FILTER_MIDPOINT 13350
|
||||
|
||||
static const unsigned int ad778x_gain[2] = { 1, 128 };
|
||||
static const unsigned int ad778x_odr_avail[2] = { 10000, 16700 };
|
||||
|
||||
struct ad7780_chip_info {
|
||||
struct iio_chan_spec channel;
|
||||
|
@ -50,7 +60,11 @@ struct ad7780_state {
|
|||
const struct ad7780_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct gpio_desc *powerdown_gpio;
|
||||
unsigned int gain;
|
||||
struct gpio_desc *gain_gpio;
|
||||
struct gpio_desc *filter_gpio;
|
||||
unsigned int gain;
|
||||
unsigned int odr;
|
||||
unsigned int int_vref_mv;
|
||||
|
||||
struct ad_sigma_delta sd;
|
||||
};
|
||||
|
@ -104,17 +118,69 @@ static int ad7780_read_raw(struct iio_dev *indio_dev,
|
|||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
if (voltage_uv < 0)
|
||||
return voltage_uv;
|
||||
*val = (voltage_uv / 1000) * st->gain;
|
||||
voltage_uv /= 1000;
|
||||
*val = voltage_uv * st->gain;
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
st->int_vref_mv = voltage_uv;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = st->odr;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7780_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long m)
|
||||
{
|
||||
struct ad7780_state *st = iio_priv(indio_dev);
|
||||
const struct ad7780_chip_info *chip_info = st->chip_info;
|
||||
unsigned long long vref;
|
||||
unsigned int full_scale, gain;
|
||||
|
||||
if (!chip_info->is_ad778x)
|
||||
return -EINVAL;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
vref = st->int_vref_mv * 1000000LL;
|
||||
full_scale = 1 << (chip_info->channel.scan_type.realbits - 1);
|
||||
gain = DIV_ROUND_CLOSEST_ULL(vref, full_scale);
|
||||
gain = DIV_ROUND_CLOSEST(gain, val2);
|
||||
st->gain = gain;
|
||||
if (gain < AD7780_GAIN_MIDPOINT)
|
||||
gain = 0;
|
||||
else
|
||||
gain = 1;
|
||||
gpiod_set_value(st->gain_gpio, gain);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (1000*val + val2/1000 < AD7780_FILTER_MIDPOINT)
|
||||
val = 0;
|
||||
else
|
||||
val = 1;
|
||||
st->odr = ad778x_odr_avail[val];
|
||||
gpiod_set_value(st->filter_gpio, val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int raw_sample)
|
||||
{
|
||||
|
@ -126,10 +192,8 @@ static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
|
|||
return -EIO;
|
||||
|
||||
if (chip_info->is_ad778x) {
|
||||
if (raw_sample & AD7780_GAIN)
|
||||
st->gain = 1;
|
||||
else
|
||||
st->gain = 128;
|
||||
st->gain = ad778x_gain[raw_sample & AD7780_GAIN];
|
||||
st->odr = ad778x_odr_avail[raw_sample & AD7780_FILTER];
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -142,30 +206,32 @@ static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
|
|||
};
|
||||
|
||||
#define AD7780_CHANNEL(bits, wordsize) \
|
||||
AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, wordsize - bits)
|
||||
AD_SD_CHANNEL(1, 0, 0, bits, 32, (wordsize) - (bits))
|
||||
#define AD7170_CHANNEL(bits, wordsize) \
|
||||
AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, (wordsize) - (bits))
|
||||
|
||||
static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
|
||||
[ID_AD7170] = {
|
||||
.channel = AD7780_CHANNEL(12, 24),
|
||||
.pattern = AD7170_PATTERN,
|
||||
.channel = AD7170_CHANNEL(12, 24),
|
||||
.pattern = AD7170_PATTERN_GOOD,
|
||||
.pattern_mask = AD7170_PATTERN_MASK,
|
||||
.is_ad778x = false,
|
||||
},
|
||||
[ID_AD7171] = {
|
||||
.channel = AD7780_CHANNEL(16, 24),
|
||||
.pattern = AD7170_PATTERN,
|
||||
.channel = AD7170_CHANNEL(16, 24),
|
||||
.pattern = AD7170_PATTERN_GOOD,
|
||||
.pattern_mask = AD7170_PATTERN_MASK,
|
||||
.is_ad778x = false,
|
||||
},
|
||||
[ID_AD7780] = {
|
||||
.channel = AD7780_CHANNEL(24, 32),
|
||||
.pattern = AD7780_PATTERN,
|
||||
.pattern = AD7780_PATTERN_GOOD,
|
||||
.pattern_mask = AD7780_PATTERN_MASK,
|
||||
.is_ad778x = true,
|
||||
},
|
||||
[ID_AD7781] = {
|
||||
.channel = AD7780_CHANNEL(20, 32),
|
||||
.pattern = AD7780_PATTERN,
|
||||
.pattern = AD7780_PATTERN_GOOD,
|
||||
.pattern_mask = AD7780_PATTERN_MASK,
|
||||
.is_ad778x = true,
|
||||
},
|
||||
|
@ -173,8 +239,47 @@ static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
|
|||
|
||||
static const struct iio_info ad7780_info = {
|
||||
.read_raw = ad7780_read_raw,
|
||||
.write_raw = ad7780_write_raw,
|
||||
};
|
||||
|
||||
static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
st->powerdown_gpio = devm_gpiod_get_optional(dev,
|
||||
"powerdown",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->powerdown_gpio)) {
|
||||
ret = PTR_ERR(st->powerdown_gpio);
|
||||
dev_err(dev, "Failed to request powerdown GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!st->chip_info->is_ad778x)
|
||||
return 0;
|
||||
|
||||
|
||||
st->gain_gpio = devm_gpiod_get_optional(dev,
|
||||
"adi,gain",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->gain_gpio)) {
|
||||
ret = PTR_ERR(st->gain_gpio);
|
||||
dev_err(dev, "Failed to request gain GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->filter_gpio = devm_gpiod_get_optional(dev,
|
||||
"adi,filter",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->filter_gpio)) {
|
||||
ret = PTR_ERR(st->filter_gpio);
|
||||
dev_err(dev, "Failed to request filter GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7780_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7780_state *st;
|
||||
|
@ -190,16 +295,6 @@ static int ad7780_probe(struct spi_device *spi)
|
|||
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
|
@ -212,14 +307,18 @@ static int ad7780_probe(struct spi_device *spi)
|
|||
indio_dev->num_channels = 1;
|
||||
indio_dev->info = &ad7780_info;
|
||||
|
||||
st->powerdown_gpio = devm_gpiod_get_optional(&spi->dev,
|
||||
"powerdown",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->powerdown_gpio)) {
|
||||
ret = PTR_ERR(st->powerdown_gpio);
|
||||
dev_err(&spi->dev, "Failed to request powerdown GPIO: %d\n",
|
||||
ret);
|
||||
goto error_disable_reg;
|
||||
ret = ad7780_init_gpios(&spi->dev, st);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer_and_trigger;
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
|
@ -24,9 +24,9 @@
|
|||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AD7923_WRITE_CR (1 << 11) /* write control register */
|
||||
#define AD7923_RANGE (1 << 1) /* range to REFin */
|
||||
#define AD7923_CODING (1 << 0) /* coding is straight binary */
|
||||
#define AD7923_WRITE_CR BIT(11) /* write control register */
|
||||
#define AD7923_RANGE BIT(1) /* range to REFin */
|
||||
#define AD7923_CODING BIT(0) /* coding is straight binary */
|
||||
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
||||
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
||||
#define AD7923_PM_MODE_OPS (3) /* normal operation */
|
||||
|
@ -40,16 +40,16 @@
|
|||
|
||||
#define AD7923_MAX_CHAN 4
|
||||
|
||||
#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
|
||||
#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
|
||||
#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
|
||||
+ ((sequence & 2) << 9))
|
||||
#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
|
||||
#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
|
||||
#define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \
|
||||
+ (((sequence) & 2) << 9))
|
||||
/* write sequence fonction */
|
||||
/* left shift for CR : bit 11 transmit in first */
|
||||
#define AD7923_SHIFT_REGISTER 4
|
||||
|
||||
/* val = value, dec = left shift, bits = number of bits of the mask */
|
||||
#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
|
||||
#define EXTRACT(val, dec, bits) (((val) >> (dec)) & ((1 << (bits)) - 1))
|
||||
|
||||
struct ad7923_state {
|
||||
struct spi_device *spi;
|
||||
|
@ -130,7 +130,7 @@ static const struct ad7923_chip_info ad7923_chip_info[] = {
|
|||
* ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
int i, cmd, len;
|
||||
|
@ -181,7 +181,7 @@ static irqreturn_t ad7923_trigger_handler(int irq, void *p)
|
|||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
@ -272,7 +272,7 @@ static int ad7923_probe(struct spi_device *spi)
|
|||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
@ -314,7 +314,7 @@ static int ad7923_probe(struct spi_device *spi)
|
|||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7923_trigger_handler, NULL);
|
||||
&ad7923_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
|
|||
struct spi_transfer t = {
|
||||
.tx_buf = data,
|
||||
.len = size + 1,
|
||||
.cs_change = sigma_delta->bus_locked,
|
||||
.cs_change = sigma_delta->keep_cs_asserted,
|
||||
};
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
@ -218,6 +218,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
|||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
sigma_delta->keep_cs_asserted = true;
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
|
||||
ret = ad_sigma_delta_set_mode(sigma_delta, mode);
|
||||
|
@ -235,9 +236,10 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
|||
ret = 0;
|
||||
}
|
||||
out:
|
||||
sigma_delta->keep_cs_asserted = false;
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -290,6 +292,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
|||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
sigma_delta->keep_cs_asserted = true;
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
|
||||
|
@ -299,9 +302,6 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
|||
ret = wait_for_completion_interruptible_timeout(
|
||||
&sigma_delta->completion, HZ);
|
||||
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -EIO;
|
||||
if (ret < 0)
|
||||
|
@ -322,7 +322,10 @@ out:
|
|||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
sigma_delta->keep_cs_asserted = false;
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret)
|
||||
|
@ -359,6 +362,8 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
|
|||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
sigma_delta->keep_cs_asserted = true;
|
||||
|
||||
ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
@ -387,6 +392,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
|
|||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
sigma_delta->keep_cs_asserted = false;
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
|
||||
sigma_delta->bus_locked = false;
|
||||
|
|
|
@ -388,8 +388,9 @@ static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
|
|||
* timeout flags.
|
||||
*/
|
||||
if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
|
||||
pr_err("%s: ADC got conversion time out interrupt: 0x%08x\n",
|
||||
dev_name(info->dev), status);
|
||||
dev_err(info->dev,
|
||||
"ADC got conversion time out interrupt: 0x%08x\n",
|
||||
status);
|
||||
status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
|
||||
writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
|
||||
}
|
||||
|
@ -433,136 +434,7 @@ static void imx7d_adc_power_down(struct imx7d_adc *info)
|
|||
writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
|
||||
}
|
||||
|
||||
static int imx7d_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx7d_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "Failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs)) {
|
||||
ret = PTR_ERR(info->regs);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to remap adc memory, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "No irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
ret = PTR_ERR(info->clk);
|
||||
dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref)) {
|
||||
ret = PTR_ERR(info->vref);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed getting reference voltage, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Can't enable adc reference top voltage, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &imx7d_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = imx7d_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the clock.\n");
|
||||
goto error_adc_clk_enable;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
imx7d_adc_isr, 0,
|
||||
dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
imx7d_adc_feature_config(info);
|
||||
imx7d_adc_hw_init(info);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
imx7d_adc_power_down(info);
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_iio_device_register:
|
||||
clk_disable_unprepare(info->clk);
|
||||
error_adc_clk_enable:
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx7d_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct imx7d_adc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
imx7d_adc_power_down(info);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx7d_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct imx7d_adc *info = iio_priv(indio_dev);
|
||||
|
||||
imx7d_adc_power_down(info);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx7d_adc_resume(struct device *dev)
|
||||
static int imx7d_adc_enable(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct imx7d_adc *info = iio_priv(indio_dev);
|
||||
|
@ -589,11 +461,112 @@ static int __maybe_unused imx7d_adc_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
|
||||
static int imx7d_adc_disable(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct imx7d_adc *info = iio_priv(indio_dev);
|
||||
|
||||
imx7d_adc_power_down(info);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __imx7d_adc_disable(void *data)
|
||||
{
|
||||
imx7d_adc_disable(data);
|
||||
}
|
||||
|
||||
static int imx7d_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx7d_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "Failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = dev;
|
||||
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs)) {
|
||||
ret = PTR_ERR(info->regs);
|
||||
dev_err(dev, "Failed to remap adc memory, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "No irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
ret = PTR_ERR(info->clk);
|
||||
dev_err(dev, "Failed getting clock, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(info->vref)) {
|
||||
ret = PTR_ERR(info->vref);
|
||||
dev_err(dev,
|
||||
"Failed getting reference voltage, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &imx7d_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = imx7d_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
|
||||
|
||||
ret = devm_request_irq(dev, irq,
|
||||
imx7d_adc_isr, 0,
|
||||
dev_name(dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
imx7d_adc_feature_config(info);
|
||||
|
||||
ret = imx7d_adc_enable(&indio_dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, __imx7d_adc_disable,
|
||||
&indio_dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable, imx7d_adc_enable);
|
||||
|
||||
static struct platform_driver imx7d_adc_driver = {
|
||||
.probe = imx7d_adc_probe,
|
||||
.remove = imx7d_adc_remove,
|
||||
.driver = {
|
||||
.name = "imx7d_adc",
|
||||
.of_match_table = imx7d_adc_match,
|
||||
|
|
|
@ -302,10 +302,8 @@ static int ingenic_adc_probe(struct platform_device *pdev)
|
|||
|
||||
mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc->base = devm_ioremap_resource(dev, mem_base);
|
||||
if (IS_ERR(adc->base)) {
|
||||
dev_err(dev, "Unable to ioremap mmio resource\n");
|
||||
if (IS_ERR(adc->base))
|
||||
return PTR_ERR(adc->base);
|
||||
}
|
||||
|
||||
adc->clk = devm_clk_get(dev, "adc");
|
||||
if (IS_ERR(adc->clk)) {
|
||||
|
|
|
@ -7,20 +7,15 @@
|
|||
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/*
|
||||
* LPC32XX registers definitions
|
||||
|
@ -52,6 +47,7 @@ struct lpc32xx_adc_state {
|
|||
void __iomem *adc_base;
|
||||
struct clk *clk;
|
||||
struct completion completion;
|
||||
struct regulator *vref;
|
||||
|
||||
u32 value;
|
||||
};
|
||||
|
@ -64,7 +60,9 @@ static int lpc32xx_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct lpc32xx_adc_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
if (mask == IIO_CHAN_INFO_RAW) {
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = clk_prepare_enable(st->clk);
|
||||
if (ret) {
|
||||
|
@ -84,22 +82,36 @@ static int lpc32xx_read_raw(struct iio_dev *indio_dev,
|
|||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = regulator_get_voltage(st->vref) / 1000;
|
||||
*val2 = 10;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info lpc32xx_adc_iio_info = {
|
||||
.read_raw = &lpc32xx_read_raw,
|
||||
};
|
||||
|
||||
#define LPC32XX_ADC_CHANNEL(_index) { \
|
||||
#define LPC32XX_ADC_CHANNEL_BASE(_index) \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.address = LPC32XXAD_IN * _index, \
|
||||
.scan_index = _index, \
|
||||
.scan_index = _index,
|
||||
|
||||
#define LPC32XX_ADC_CHANNEL(_index) { \
|
||||
LPC32XX_ADC_CHANNEL_BASE(_index) \
|
||||
}
|
||||
|
||||
#define LPC32XX_ADC_SCALE_CHANNEL(_index) { \
|
||||
LPC32XX_ADC_CHANNEL_BASE(_index) \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
|
||||
|
@ -108,6 +120,12 @@ static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
|
|||
LPC32XX_ADC_CHANNEL(2),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec lpc32xx_adc_iio_scale_channels[] = {
|
||||
LPC32XX_ADC_SCALE_CHANNEL(0),
|
||||
LPC32XX_ADC_SCALE_CHANNEL(1),
|
||||
LPC32XX_ADC_SCALE_CHANNEL(2),
|
||||
};
|
||||
|
||||
static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct lpc32xx_adc_state *st = dev_id;
|
||||
|
@ -166,6 +184,15 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
|||
return retval;
|
||||
}
|
||||
|
||||
st->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(st->vref)) {
|
||||
iodev->channels = lpc32xx_adc_iio_channels;
|
||||
dev_info(&pdev->dev,
|
||||
"Missing vref regulator: No scaling available\n");
|
||||
} else {
|
||||
iodev->channels = lpc32xx_adc_iio_scale_channels;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, iodev);
|
||||
|
||||
init_completion(&st->completion);
|
||||
|
@ -174,7 +201,6 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
|||
iodev->dev.parent = &pdev->dev;
|
||||
iodev->info = &lpc32xx_adc_iio_info;
|
||||
iodev->modes = INDIO_DIRECT_MODE;
|
||||
iodev->channels = lpc32xx_adc_iio_channels;
|
||||
iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
|
||||
|
||||
retval = devm_iio_device_register(&pdev->dev, iodev);
|
||||
|
|
|
@ -1150,6 +1150,11 @@ static const struct meson_sar_adc_data meson_sar_adc_axg_data = {
|
|||
.name = "meson-axg-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_g12a_data = {
|
||||
.param = &meson_sar_adc_gxl_param,
|
||||
.name = "meson-g12a-saradc",
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_sar_adc_of_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson8-saradc",
|
||||
|
@ -1175,6 +1180,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = {
|
|||
}, {
|
||||
.compatible = "amlogic,meson-axg-saradc",
|
||||
.data = &meson_sar_adc_axg_data,
|
||||
}, {
|
||||
.compatible = "amlogic,meson-g12a-saradc",
|
||||
.data = &meson_sar_adc_g12a_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -465,6 +465,8 @@ static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
|
|||
|
||||
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
|
||||
iio->id);
|
||||
if (!trig)
|
||||
return -ENOMEM;
|
||||
|
||||
trig->dev.parent = adc->dev;
|
||||
iio_trigger_set_drvdata(trig, iio);
|
||||
|
|
|
@ -664,6 +664,7 @@ static const struct of_device_id adc5_match_table[] = {
|
|||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc5_match_table);
|
||||
|
||||
static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/hw-consumer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/timer/stm32-lptim-trigger.h>
|
||||
#include <linux/iio/timer/stm32-timer-trigger.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
|
@ -38,6 +43,11 @@
|
|||
#define DFSDM_MAX_RES BIT(31)
|
||||
#define DFSDM_DATA_RES BIT(23)
|
||||
|
||||
/* Filter configuration */
|
||||
#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
|
||||
DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \
|
||||
DFSDM_CR1_JSCAN_MASK)
|
||||
|
||||
enum sd_converter_type {
|
||||
DFSDM_AUDIO,
|
||||
DFSDM_IIO,
|
||||
|
@ -54,6 +64,8 @@ struct stm32_dfsdm_adc {
|
|||
struct stm32_dfsdm *dfsdm;
|
||||
const struct stm32_dfsdm_dev_data *dev_data;
|
||||
unsigned int fl_id;
|
||||
unsigned int nconv;
|
||||
unsigned long smask;
|
||||
|
||||
/* ADC specific */
|
||||
unsigned int oversamp;
|
||||
|
@ -114,6 +126,61 @@ static int stm32_dfsdm_str2val(const char *str,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct stm32_dfsdm_trig_info - DFSDM trigger info
|
||||
* @name: name of the trigger, corresponding to its source
|
||||
* @jextsel: trigger signal selection
|
||||
*/
|
||||
struct stm32_dfsdm_trig_info {
|
||||
const char *name;
|
||||
unsigned int jextsel;
|
||||
};
|
||||
|
||||
/* hardware injected trigger enable, edge selection */
|
||||
enum stm32_dfsdm_jexten {
|
||||
STM32_DFSDM_JEXTEN_DISABLED,
|
||||
STM32_DFSDM_JEXTEN_RISING_EDGE,
|
||||
STM32_DFSDM_JEXTEN_FALLING_EDGE,
|
||||
STM32_DFSDM_EXTEN_BOTH_EDGES,
|
||||
};
|
||||
|
||||
static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
|
||||
{ TIM1_TRGO, 0 },
|
||||
{ TIM1_TRGO2, 1 },
|
||||
{ TIM8_TRGO, 2 },
|
||||
{ TIM8_TRGO2, 3 },
|
||||
{ TIM3_TRGO, 4 },
|
||||
{ TIM4_TRGO, 5 },
|
||||
{ TIM16_OC1, 6 },
|
||||
{ TIM6_TRGO, 7 },
|
||||
{ TIM7_TRGO, 8 },
|
||||
{ LPTIM1_OUT, 26 },
|
||||
{ LPTIM2_OUT, 27 },
|
||||
{ LPTIM3_OUT, 28 },
|
||||
{},
|
||||
};
|
||||
|
||||
static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* lookup triggers registered by stm32 timer trigger driver */
|
||||
for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
|
||||
/**
|
||||
* Checking both stm32 timer trigger type and trig name
|
||||
* should be safe against arbitrary trigger names.
|
||||
*/
|
||||
if ((is_stm32_timer_trigger(trig) ||
|
||||
is_stm32_lptim_trigger(trig)) &&
|
||||
!strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
|
||||
return stm32_dfsdm_trigs[i].jextsel;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
|
||||
unsigned int fast, unsigned int oversamp)
|
||||
{
|
||||
|
@ -200,19 +267,39 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm,
|
||||
unsigned int ch_id)
|
||||
static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
|
||||
{
|
||||
return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
|
||||
DFSDM_CHCFGR1_CHEN_MASK,
|
||||
DFSDM_CHCFGR1_CHEN(1));
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int bit;
|
||||
int ret;
|
||||
|
||||
for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
|
||||
chan = indio_dev->channels + bit;
|
||||
ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
|
||||
DFSDM_CHCFGR1_CHEN_MASK,
|
||||
DFSDM_CHCFGR1_CHEN(1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm,
|
||||
unsigned int ch_id)
|
||||
static void stm32_dfsdm_stop_channel(struct stm32_dfsdm_adc *adc)
|
||||
{
|
||||
regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
|
||||
DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0));
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int bit;
|
||||
|
||||
for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
|
||||
chan = indio_dev->channels + bit;
|
||||
regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
|
||||
DFSDM_CHCFGR1_CHEN_MASK,
|
||||
DFSDM_CHCFGR1_CHEN(0));
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
|
||||
|
@ -237,9 +324,11 @@ static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
|
|||
DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
|
||||
unsigned int fl_id)
|
||||
static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
|
||||
unsigned int fl_id,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct stm32_dfsdm *dfsdm = adc->dfsdm;
|
||||
int ret;
|
||||
|
||||
/* Enable filter */
|
||||
|
@ -248,7 +337,11 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Start conversion */
|
||||
/* Nothing more to do for injected (scan mode/triggered) conversions */
|
||||
if (adc->nconv > 1 || trig)
|
||||
return 0;
|
||||
|
||||
/* Software start (single or continuous) regular conversion */
|
||||
return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
|
||||
DFSDM_CR1_RSWSTART_MASK,
|
||||
DFSDM_CR1_RSWSTART(1));
|
||||
|
@ -262,11 +355,45 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
|
|||
DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
|
||||
unsigned int fl_id, unsigned int ch_id)
|
||||
static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
|
||||
unsigned int fl_id,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct regmap *regmap = dfsdm->regmap;
|
||||
struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
|
||||
int ret;
|
||||
|
||||
if (trig) {
|
||||
ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set trigger source and polarity (default to rising edge) */
|
||||
jextsel = ret;
|
||||
jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
|
||||
DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
|
||||
DFSDM_CR1_JEXTSEL(jextsel) |
|
||||
DFSDM_CR1_JEXTEN(jexten));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
|
||||
unsigned int fl_id,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
|
||||
u32 cr1;
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int bit, jchg = 0;
|
||||
int ret;
|
||||
|
||||
/* Average integrator oversampling */
|
||||
|
@ -286,15 +413,68 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* No scan mode supported for the moment */
|
||||
ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
|
||||
DFSDM_CR1_RCH(ch_id));
|
||||
ret = stm32_dfsdm_filter_set_trig(adc, fl_id, trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
|
||||
DFSDM_CR1_RSYNC_MASK,
|
||||
DFSDM_CR1_RSYNC(fl->sync_mode));
|
||||
/*
|
||||
* DFSDM modes configuration W.R.T audio/iio type modes
|
||||
* ----------------------------------------------------------------
|
||||
* Modes | regular | regular | injected | injected |
|
||||
* | | continuous | | + scan |
|
||||
* --------------|---------|--------------|----------|------------|
|
||||
* single conv | x | | | |
|
||||
* (1 chan) | | | | |
|
||||
* --------------|---------|--------------|----------|------------|
|
||||
* 1 Audio chan | | sample freq | | |
|
||||
* | | or sync_mode | | |
|
||||
* --------------|---------|--------------|----------|------------|
|
||||
* 1 IIO chan | | sample freq | trigger | |
|
||||
* | | or sync_mode | | |
|
||||
* --------------|---------|--------------|----------|------------|
|
||||
* 2+ IIO chans | | | | trigger or |
|
||||
* | | | | sync_mode |
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
if (adc->nconv == 1 && !trig) {
|
||||
bit = __ffs(adc->smask);
|
||||
chan = indio_dev->channels + bit;
|
||||
|
||||
/* Use regular conversion for single channel without trigger */
|
||||
cr1 = DFSDM_CR1_RCH(chan->channel);
|
||||
|
||||
/* Continuous conversions triggered by SPI clk in buffer mode */
|
||||
if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)
|
||||
cr1 |= DFSDM_CR1_RCONT(1);
|
||||
|
||||
cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
|
||||
} else {
|
||||
/* Use injected conversion for multiple channels */
|
||||
for_each_set_bit(bit, &adc->smask,
|
||||
sizeof(adc->smask) * BITS_PER_BYTE) {
|
||||
chan = indio_dev->channels + bit;
|
||||
jchg |= BIT(chan->channel);
|
||||
}
|
||||
ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Use scan mode for multiple channels */
|
||||
cr1 = DFSDM_CR1_JSCAN((adc->nconv > 1) ? 1 : 0);
|
||||
|
||||
/*
|
||||
* Continuous conversions not supported in injected mode,
|
||||
* either use:
|
||||
* - conversions in sync with filter 0
|
||||
* - triggered conversions
|
||||
*/
|
||||
if (!fl->sync_mode && !trig)
|
||||
return -EINVAL;
|
||||
cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
|
||||
}
|
||||
|
||||
return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
|
||||
cr1);
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
|
||||
|
@ -378,13 +558,38 @@ static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
|
||||
}
|
||||
|
||||
static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
|
||||
unsigned int sample_freq,
|
||||
unsigned int spi_freq)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
||||
unsigned int oversamp;
|
||||
int ret;
|
||||
|
||||
oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
|
||||
if (spi_freq % sample_freq)
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"Rate not accurate. requested (%u), actual (%u)\n",
|
||||
sample_freq, spi_freq / oversamp);
|
||||
|
||||
ret = stm32_dfsdm_set_osrs(fl, 0, oversamp);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "No filter parameters that match!\n");
|
||||
return ret;
|
||||
}
|
||||
adc->sample_freq = spi_freq / oversamp;
|
||||
adc->oversamp = oversamp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
|
||||
uintptr_t priv,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
||||
struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
|
||||
unsigned int sample_freq = adc->sample_freq;
|
||||
unsigned int spi_freq;
|
||||
|
@ -403,17 +608,9 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
|
||||
if (sample_freq) {
|
||||
if (spi_freq % sample_freq)
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Sampling rate not accurate (%d)\n",
|
||||
spi_freq / (spi_freq / sample_freq));
|
||||
|
||||
ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"No filter parameters that match!\n");
|
||||
ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
adc->spi_freq = spi_freq;
|
||||
|
||||
|
@ -421,72 +618,44 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
|
||||
const struct iio_chan_spec *chan,
|
||||
bool dma)
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
int ret;
|
||||
unsigned int dma_en = 0, cont_en = 0;
|
||||
|
||||
ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
|
||||
ret = stm32_dfsdm_start_channel(adc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
|
||||
chan->channel);
|
||||
ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
|
||||
if (ret < 0)
|
||||
goto stop_channels;
|
||||
|
||||
if (dma) {
|
||||
/* Enable DMA transfer*/
|
||||
dma_en = DFSDM_CR1_RDMAEN(1);
|
||||
/* Enable conversion triggered by SPI clock*/
|
||||
cont_en = DFSDM_CR1_RCONT(1);
|
||||
}
|
||||
/* Enable DMA transfer*/
|
||||
ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RDMAEN_MASK, dma_en);
|
||||
ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
|
||||
if (ret < 0)
|
||||
goto stop_channels;
|
||||
|
||||
/* Enable conversion triggered by SPI clock*/
|
||||
ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RCONT_MASK, cont_en);
|
||||
if (ret < 0)
|
||||
goto stop_channels;
|
||||
|
||||
ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
|
||||
if (ret < 0)
|
||||
goto stop_channels;
|
||||
goto filter_unconfigure;
|
||||
|
||||
return 0;
|
||||
|
||||
filter_unconfigure:
|
||||
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_CFG_MASK, 0);
|
||||
stop_channels:
|
||||
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RDMAEN_MASK, 0);
|
||||
|
||||
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RCONT_MASK, 0);
|
||||
stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
|
||||
stm32_dfsdm_stop_channel(adc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
|
||||
const struct iio_chan_spec *chan)
|
||||
static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
|
||||
{
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
|
||||
stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
|
||||
|
||||
/* Clean conversion options */
|
||||
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RDMAEN_MASK, 0);
|
||||
DFSDM_CR1_CFG_MASK, 0);
|
||||
|
||||
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RCONT_MASK, 0);
|
||||
|
||||
stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
|
||||
stm32_dfsdm_stop_channel(adc);
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
|
||||
|
@ -494,6 +663,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
|
||||
unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;
|
||||
|
||||
/*
|
||||
* DMA cyclic transfers are used, buffer is split into two periods.
|
||||
|
@ -502,7 +672,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
|
|||
* - one buffer (period) driver pushed to ASoC side.
|
||||
*/
|
||||
watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
|
||||
adc->buf_sz = watermark * 2;
|
||||
adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -532,13 +702,41 @@ static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_audio_dma_buffer_done(void *data)
|
||||
static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
int available = stm32_dfsdm_adc_dma_residue(adc);
|
||||
|
||||
while (available >= indio_dev->scan_bytes) {
|
||||
u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
pf->timestamp);
|
||||
available -= indio_dev->scan_bytes;
|
||||
adc->bufi += indio_dev->scan_bytes;
|
||||
if (adc->bufi >= adc->buf_sz)
|
||||
adc->bufi = 0;
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_dma_buffer_done(void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
int available = stm32_dfsdm_adc_dma_residue(adc);
|
||||
size_t old_pos;
|
||||
|
||||
if (indio_dev->currentmode & INDIO_BUFFER_TRIGGERED) {
|
||||
iio_trigger_poll_chained(indio_dev->trig);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: In Kernel interface does not support cyclic DMA buffer,and
|
||||
* offers only an interface to push data samples per samples.
|
||||
|
@ -566,6 +764,9 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
|
|||
adc->bufi = 0;
|
||||
old_pos = 0;
|
||||
}
|
||||
/* regular iio buffer without trigger */
|
||||
if (adc->dev_data->type == DFSDM_IIO)
|
||||
iio_push_to_buffers(indio_dev, buffer);
|
||||
}
|
||||
if (adc->cb)
|
||||
adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
|
||||
|
@ -575,6 +776,10 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
|
|||
static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct dma_slave_config config = {
|
||||
.src_addr = (dma_addr_t)adc->dfsdm->phys_base,
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
};
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
dma_cookie_t cookie;
|
||||
int ret;
|
||||
|
@ -585,6 +790,14 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
|
|||
dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
|
||||
adc->buf_sz, adc->buf_sz / 2);
|
||||
|
||||
if (adc->nconv == 1 && !indio_dev->trig)
|
||||
config.src_addr += DFSDM_RDATAR(adc->fl_id);
|
||||
else
|
||||
config.src_addr += DFSDM_JDATAR(adc->fl_id);
|
||||
ret = dmaengine_slave_config(adc->dma_chan, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Prepare a DMA cyclic transaction */
|
||||
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
|
||||
adc->dma_buf,
|
||||
|
@ -594,71 +807,154 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
|
|||
if (!desc)
|
||||
return -EBUSY;
|
||||
|
||||
desc->callback = stm32_dfsdm_audio_dma_buffer_done;
|
||||
desc->callback = stm32_dfsdm_dma_buffer_done;
|
||||
desc->callback_param = indio_dev;
|
||||
|
||||
cookie = dmaengine_submit(desc);
|
||||
ret = dma_submit_error(cookie);
|
||||
if (ret) {
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_stop_dma;
|
||||
|
||||
/* Issue pending DMA requests */
|
||||
dma_async_issue_pending(adc->dma_chan);
|
||||
|
||||
if (adc->nconv == 1 && !indio_dev->trig) {
|
||||
/* Enable regular DMA transfer*/
|
||||
ret = regmap_update_bits(adc->dfsdm->regmap,
|
||||
DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RDMAEN_MASK,
|
||||
DFSDM_CR1_RDMAEN_MASK);
|
||||
} else {
|
||||
/* Enable injected DMA transfer*/
|
||||
ret = regmap_update_bits(adc->dfsdm->regmap,
|
||||
DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_JDMAEN_MASK,
|
||||
DFSDM_CR1_JDMAEN_MASK);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto err_stop_dma;
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_dma:
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
if (!adc->dma_chan)
|
||||
return;
|
||||
|
||||
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
|
||||
DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0);
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
|
||||
adc->smask = *scan_mask;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
|
||||
static int __stm32_dfsdm_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *chan = &indio_dev->channels[0];
|
||||
int ret;
|
||||
|
||||
/* Reset adc buffer index */
|
||||
adc->bufi = 0;
|
||||
|
||||
if (adc->hwc) {
|
||||
ret = iio_hw_consumer_enable(adc->hwc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_stop_hwc;
|
||||
|
||||
ret = stm32_dfsdm_start_conv(adc, chan, true);
|
||||
ret = stm32_dfsdm_adc_dma_start(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't start conversion\n");
|
||||
dev_err(&indio_dev->dev, "Can't start DMA\n");
|
||||
goto stop_dfsdm;
|
||||
}
|
||||
|
||||
if (adc->dma_chan) {
|
||||
ret = stm32_dfsdm_adc_dma_start(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't start DMA\n");
|
||||
goto err_stop_conv;
|
||||
}
|
||||
ret = stm32_dfsdm_start_conv(adc, indio_dev->trig);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't start conversion\n");
|
||||
goto err_stop_dma;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_conv:
|
||||
stm32_dfsdm_stop_conv(adc, chan);
|
||||
err_stop_dma:
|
||||
stm32_dfsdm_adc_dma_stop(indio_dev);
|
||||
stop_dfsdm:
|
||||
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
|
||||
err_stop_hwc:
|
||||
if (adc->hwc)
|
||||
iio_hw_consumer_disable(adc->hwc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
|
||||
static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = __stm32_dfsdm_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_predisable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_predisable:
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __stm32_dfsdm_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *chan = &indio_dev->channels[0];
|
||||
|
||||
if (adc->dma_chan)
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
stm32_dfsdm_stop_conv(adc);
|
||||
|
||||
stm32_dfsdm_stop_conv(adc, chan);
|
||||
stm32_dfsdm_adc_dma_stop(indio_dev);
|
||||
|
||||
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
|
||||
|
||||
if (adc->hwc)
|
||||
iio_hw_consumer_disable(adc->hwc);
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
__stm32_dfsdm_predisable(indio_dev);
|
||||
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -736,7 +1032,9 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
goto stop_dfsdm;
|
||||
|
||||
ret = stm32_dfsdm_start_conv(adc, chan, false);
|
||||
adc->nconv = 1;
|
||||
adc->smask = BIT(chan->scan_index);
|
||||
ret = stm32_dfsdm_start_conv(adc, NULL);
|
||||
if (ret < 0) {
|
||||
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
|
||||
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
|
||||
|
@ -757,7 +1055,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
|
|||
else
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
stm32_dfsdm_stop_conv(adc, chan);
|
||||
stm32_dfsdm_stop_conv(adc);
|
||||
|
||||
stop_dfsdm:
|
||||
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
|
||||
|
@ -777,16 +1075,23 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = stm32_dfsdm_set_osrs(fl, 0, val);
|
||||
if (!ret)
|
||||
adc->oversamp = val;
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (ch->src) {
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
|
||||
spi_freq = adc->dfsdm->spi_master_freq;
|
||||
|
@ -799,20 +1104,9 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
|||
spi_freq = adc->spi_freq;
|
||||
}
|
||||
|
||||
if (spi_freq % val)
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Sampling rate not accurate (%d)\n",
|
||||
spi_freq / (spi_freq / val));
|
||||
|
||||
ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / val));
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Not able to find parameter that match!\n");
|
||||
return ret;
|
||||
}
|
||||
adc->sample_freq = val;
|
||||
|
||||
return 0;
|
||||
ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -827,11 +1121,15 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = iio_hw_consumer_enable(adc->hwc);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"%s: IIO enable failed (channel %d)\n",
|
||||
__func__, chan->channel);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
|
||||
|
@ -840,8 +1138,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
|
|||
dev_err(&indio_dev->dev,
|
||||
"%s: Conversion failed (channel %d)\n",
|
||||
__func__, chan->channel);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
|
@ -858,15 +1158,25 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static const struct iio_info stm32_dfsdm_info_audio = {
|
||||
.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
|
||||
.read_raw = stm32_dfsdm_read_raw,
|
||||
.write_raw = stm32_dfsdm_write_raw,
|
||||
.update_scan_mode = stm32_dfsdm_update_scan_mode,
|
||||
};
|
||||
|
||||
static const struct iio_info stm32_dfsdm_info_adc = {
|
||||
.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
|
||||
.read_raw = stm32_dfsdm_read_raw,
|
||||
.write_raw = stm32_dfsdm_write_raw,
|
||||
.update_scan_mode = stm32_dfsdm_update_scan_mode,
|
||||
.validate_trigger = stm32_dfsdm_validate_trigger,
|
||||
};
|
||||
|
||||
static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
|
||||
|
@ -926,12 +1236,6 @@ static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
|
|||
static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct dma_slave_config config = {
|
||||
.src_addr = (dma_addr_t)adc->dfsdm->phys_base +
|
||||
DFSDM_RDATAR(adc->fl_id),
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
};
|
||||
int ret;
|
||||
|
||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
||||
if (!adc->dma_chan)
|
||||
|
@ -941,23 +1245,14 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
|
|||
DFSDM_DMA_BUFFER_SIZE,
|
||||
&adc->dma_buf, GFP_KERNEL);
|
||||
if (!adc->rx_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
dma_release_channel(adc->dma_chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(adc->dma_chan, &config);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->dma_buf);
|
||||
err_release:
|
||||
dma_release_channel(adc->dma_chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
|
@ -978,7 +1273,8 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
|
|||
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
|
||||
*/
|
||||
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
|
||||
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
|
||||
if (adc->dev_data->type == DFSDM_AUDIO) {
|
||||
ch->scan_type.sign = 's';
|
||||
|
@ -1000,9 +1296,6 @@ static int stm32_dfsdm_audio_init(struct iio_dev *indio_dev)
|
|||
struct stm32_dfsdm_channel *d_ch;
|
||||
int ret;
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
|
||||
|
||||
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
|
||||
if (!ch)
|
||||
return -ENOMEM;
|
||||
|
@ -1070,6 +1363,25 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
|
|||
|
||||
init_completion(&adc->completion);
|
||||
|
||||
/* Optionally request DMA */
|
||||
if (stm32_dfsdm_dma_request(indio_dev)) {
|
||||
dev_dbg(&indio_dev->dev, "No DMA support\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&stm32_dfsdm_adc_trigger_handler,
|
||||
&stm32_dfsdm_buffer_setup_ops);
|
||||
if (ret) {
|
||||
stm32_dfsdm_dma_release(indio_dev);
|
||||
dev_err(&indio_dev->dev, "buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* lptimer/timer hardware triggers */
|
||||
indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1429,7 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
|||
|
||||
iio->dev.parent = dev;
|
||||
iio->dev.of_node = np;
|
||||
iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
|
||||
iio->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
platform_set_drvdata(pdev, adc);
|
||||
|
||||
|
@ -1203,10 +1515,48 @@ static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
__stm32_dfsdm_predisable(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
const struct iio_chan_spec *chan;
|
||||
struct stm32_dfsdm_channel *ch;
|
||||
int i, ret;
|
||||
|
||||
/* restore channels configuration */
|
||||
for (i = 0; i < indio_dev->num_channels; i++) {
|
||||
chan = indio_dev->channels + i;
|
||||
ch = &adc->dfsdm->ch_list[chan->channel];
|
||||
ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
__stm32_dfsdm_postenable(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
|
||||
stm32_dfsdm_adc_suspend, stm32_dfsdm_adc_resume);
|
||||
|
||||
static struct platform_driver stm32_dfsdm_adc_driver = {
|
||||
.driver = {
|
||||
.name = "stm32-dfsdm-adc",
|
||||
.of_match_table = stm32_dfsdm_adc_match,
|
||||
.pm = &stm32_dfsdm_adc_pm_ops,
|
||||
},
|
||||
.probe = stm32_dfsdm_adc_probe,
|
||||
.remove = stm32_dfsdm_adc_remove,
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -90,6 +92,36 @@ struct dfsdm_priv {
|
|||
struct clk *aclk; /* audio clock */
|
||||
};
|
||||
|
||||
static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm)
|
||||
{
|
||||
return container_of(dfsdm, struct dfsdm_priv, dfsdm);
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm)
|
||||
{
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret || !priv->aclk)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->aclk);
|
||||
if (ret)
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
|
||||
{
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
|
||||
if (priv->aclk)
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_dfsdm_start_dfsdm - start global dfsdm interface.
|
||||
*
|
||||
|
@ -98,24 +130,17 @@ struct dfsdm_priv {
|
|||
*/
|
||||
int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
|
||||
{
|
||||
struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
unsigned int clk_div = priv->spi_clk_out_div, clk_src;
|
||||
int ret;
|
||||
|
||||
if (atomic_inc_return(&priv->n_active_ch) == 1) {
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to start clock\n");
|
||||
pm_runtime_put_noidle(dev);
|
||||
goto error_ret;
|
||||
}
|
||||
if (priv->aclk) {
|
||||
ret = clk_prepare_enable(priv->aclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to start audio clock\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
|
||||
clk_src = priv->aclk ? 1 : 0;
|
||||
|
@ -123,21 +148,21 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
|
|||
DFSDM_CHCFGR1_CKOUTSRC_MASK,
|
||||
DFSDM_CHCFGR1_CKOUTSRC(clk_src));
|
||||
if (ret < 0)
|
||||
goto disable_aclk;
|
||||
goto pm_put;
|
||||
|
||||
/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
|
||||
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
|
||||
DFSDM_CHCFGR1_CKOUTDIV_MASK,
|
||||
DFSDM_CHCFGR1_CKOUTDIV(clk_div));
|
||||
if (ret < 0)
|
||||
goto disable_aclk;
|
||||
goto pm_put;
|
||||
|
||||
/* Global enable of DFSDM interface */
|
||||
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
|
||||
DFSDM_CHCFGR1_DFSDMEN_MASK,
|
||||
DFSDM_CHCFGR1_DFSDMEN(1));
|
||||
if (ret < 0)
|
||||
goto disable_aclk;
|
||||
goto pm_put;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
|
||||
|
@ -145,11 +170,8 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
|
|||
|
||||
return 0;
|
||||
|
||||
disable_aclk:
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
pm_put:
|
||||
pm_runtime_put_sync(dev);
|
||||
error_ret:
|
||||
atomic_dec(&priv->n_active_ch);
|
||||
|
||||
|
@ -165,7 +187,7 @@ EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm);
|
|||
*/
|
||||
int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
|
||||
{
|
||||
struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
int ret;
|
||||
|
||||
if (atomic_dec_and_test(&priv->n_active_ch)) {
|
||||
|
@ -183,9 +205,7 @@ int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
if (priv->aclk)
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
pm_runtime_put_sync(&priv->pdev->dev);
|
||||
}
|
||||
dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
|
||||
atomic_read(&priv->n_active_ch));
|
||||
|
@ -199,7 +219,7 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
|
|||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
unsigned long clk_freq;
|
||||
unsigned long clk_freq, divider;
|
||||
unsigned int spi_freq, rem;
|
||||
int ret;
|
||||
|
||||
|
@ -243,13 +263,20 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
|
||||
if (!priv->spi_clk_out_div) {
|
||||
/* spi_clk_out_div == 0 means ckout is OFF */
|
||||
divider = div_u64_rem(clk_freq, spi_freq, &rem);
|
||||
/* Round up divider when ckout isn't precise, not to exceed spi_freq */
|
||||
if (rem)
|
||||
divider++;
|
||||
|
||||
/* programmable divider is in range of [2:256] */
|
||||
if (divider < 2 || divider > 256) {
|
||||
dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->dfsdm.spi_master_freq = spi_freq;
|
||||
|
||||
/* SPI clock output divider is: divider = CKOUTDIV + 1 */
|
||||
priv->spi_clk_out_div = divider - 1;
|
||||
priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);
|
||||
|
||||
if (rem) {
|
||||
dev_warn(&pdev->dev, "SPI clock not accurate\n");
|
||||
|
@ -318,14 +345,111 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, dfsdm);
|
||||
|
||||
return devm_of_platform_populate(&pdev->dev);
|
||||
ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to start clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
if (ret)
|
||||
goto pm_put;
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
pm_put:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
stm32_dfsdm_clk_disable_unprepare(dfsdm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_core_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
stm32_dfsdm_clk_disable_unprepare(dfsdm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Balance devm_regmap_init_mmio_clk() clk_prepare() */
|
||||
clk_unprepare(priv->clk);
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
|
||||
|
||||
stm32_dfsdm_clk_disable_unprepare(dfsdm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
|
||||
|
||||
return stm32_dfsdm_clk_prepare_enable(dfsdm);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend,
|
||||
stm32_dfsdm_core_resume)
|
||||
SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
|
||||
stm32_dfsdm_core_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver stm32_dfsdm_driver = {
|
||||
.probe = stm32_dfsdm_probe,
|
||||
.remove = stm32_dfsdm_core_remove,
|
||||
.driver = {
|
||||
.name = "stm32-dfsdm",
|
||||
.of_match_table = stm32_dfsdm_of_match,
|
||||
.pm = &stm32_dfsdm_core_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -184,9 +184,6 @@ static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
|
|||
struct stmpe_adc *info = (struct stmpe_adc *)dev_id;
|
||||
u16 data;
|
||||
|
||||
if (info->channel > STMPE_TEMP_CHANNEL)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (info->channel <= STMPE_ADC_LAST_NR) {
|
||||
int int_sta;
|
||||
|
||||
|
@ -205,6 +202,8 @@ static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
|
|||
/* Read value */
|
||||
stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2,
|
||||
(u8 *) &data);
|
||||
} else {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
info->value = (u32) be16_to_cpu(data);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -36,12 +37,15 @@
|
|||
*/
|
||||
#define TI_ADS7950_VA_MV_ACPI_DEFAULT 5000
|
||||
|
||||
#define TI_ADS7950_CR_GPIO BIT(14)
|
||||
#define TI_ADS7950_CR_MANUAL BIT(12)
|
||||
#define TI_ADS7950_CR_WRITE BIT(11)
|
||||
#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7)
|
||||
#define TI_ADS7950_CR_RANGE_5V BIT(6)
|
||||
#define TI_ADS7950_CR_GPIO_DATA BIT(4)
|
||||
|
||||
#define TI_ADS7950_MAX_CHAN 16
|
||||
#define TI_ADS7950_NUM_GPIOS 4
|
||||
|
||||
#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16))
|
||||
|
||||
|
@ -49,6 +53,16 @@
|
|||
#define TI_ADS7950_EXTRACT(val, dec, bits) \
|
||||
(((val) >> (dec)) & ((1 << (bits)) - 1))
|
||||
|
||||
#define TI_ADS7950_MAN_CMD(cmd) (TI_ADS7950_CR_MANUAL | (cmd))
|
||||
#define TI_ADS7950_GPIO_CMD(cmd) (TI_ADS7950_CR_GPIO | (cmd))
|
||||
|
||||
/* Manual mode configuration */
|
||||
#define TI_ADS7950_MAN_CMD_SETTINGS(st) \
|
||||
(TI_ADS7950_MAN_CMD(TI_ADS7950_CR_WRITE | st->cmd_settings_bitmask))
|
||||
/* GPIO mode configuration */
|
||||
#define TI_ADS7950_GPIO_CMD_SETTINGS(st) \
|
||||
(TI_ADS7950_GPIO_CMD(st->gpio_cmd_settings_bitmask))
|
||||
|
||||
struct ti_ads7950_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer;
|
||||
|
@ -56,10 +70,36 @@ struct ti_ads7950_state {
|
|||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
/* Lock to protect the spi xfer buffers */
|
||||
struct mutex slock;
|
||||
struct gpio_chip chip;
|
||||
|
||||
struct regulator *reg;
|
||||
unsigned int vref_mv;
|
||||
|
||||
unsigned int settings;
|
||||
/*
|
||||
* Bitmask of lower 7 bits used for configuration
|
||||
* These bits only can be written when TI_ADS7950_CR_WRITE
|
||||
* is set, otherwise it retains its original state.
|
||||
* [0-3] GPIO signal
|
||||
* [4] Set following frame to return GPIO signal values
|
||||
* [5] Powers down device
|
||||
* [6] Sets Vref range1(2.5v) or range2(5v)
|
||||
*
|
||||
* Bits present on Manual/Auto1/Auto2 commands
|
||||
*/
|
||||
unsigned int cmd_settings_bitmask;
|
||||
|
||||
/*
|
||||
* Bitmask of GPIO command
|
||||
* [0-3] GPIO direction
|
||||
* [4-6] Different GPIO alarm mode configurations
|
||||
* [7] GPIO 2 as device range input
|
||||
* [8] GPIO 3 as device power down input
|
||||
* [9] Reset all registers
|
||||
* [10-11] N/A
|
||||
*/
|
||||
unsigned int gpio_cmd_settings_bitmask;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
|
@ -248,7 +288,7 @@ static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
|
|||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
|
||||
cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i));
|
||||
st->tx_buf[len++] = cmd;
|
||||
}
|
||||
|
||||
|
@ -268,6 +308,7 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
|
|||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->slock);
|
||||
ret = spi_sync(st->spi, &st->ring_msg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -276,6 +317,7 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
|
|||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out:
|
||||
mutex_unlock(&st->slock);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -286,9 +328,8 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
|||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret, cmd;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
|
||||
mutex_lock(&st->slock);
|
||||
cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(ch));
|
||||
st->single_tx = cmd;
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
|
@ -298,7 +339,7 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
|||
ret = st->single_rx;
|
||||
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -317,7 +358,7 @@ static int ti_ads7950_get_range(struct ti_ads7950_state *st)
|
|||
vref /= 1000;
|
||||
}
|
||||
|
||||
if (st->settings & TI_ADS7950_CR_RANGE_5V)
|
||||
if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V)
|
||||
vref *= 2;
|
||||
|
||||
return vref;
|
||||
|
@ -362,6 +403,132 @@ static const struct iio_info ti_ads7950_info = {
|
|||
.update_scan_mode = ti_ads7950_update_scan_mode,
|
||||
};
|
||||
|
||||
static void ti_ads7950_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct ti_ads7950_state *st = gpiochip_get_data(chip);
|
||||
|
||||
mutex_lock(&st->slock);
|
||||
|
||||
if (value)
|
||||
st->cmd_settings_bitmask |= BIT(offset);
|
||||
else
|
||||
st->cmd_settings_bitmask &= ~BIT(offset);
|
||||
|
||||
st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
|
||||
spi_sync(st->spi, &st->scan_single_msg);
|
||||
|
||||
mutex_unlock(&st->slock);
|
||||
}
|
||||
|
||||
static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct ti_ads7950_state *st = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->slock);
|
||||
|
||||
/* If set as output, return the output */
|
||||
if (st->gpio_cmd_settings_bitmask & BIT(offset)) {
|
||||
ret = st->cmd_settings_bitmask & BIT(offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* GPIO data bit sets SDO bits 12-15 to GPIO input */
|
||||
st->cmd_settings_bitmask |= TI_ADS7950_CR_GPIO_DATA;
|
||||
st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0;
|
||||
|
||||
/* Revert back to original settings */
|
||||
st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA;
|
||||
st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
mutex_unlock(&st->slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_ads7950_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct ti_ads7950_state *st = gpiochip_get_data(chip);
|
||||
|
||||
/* Bitmask is inverted from GPIO framework 0=input/1=output */
|
||||
return !(st->gpio_cmd_settings_bitmask & BIT(offset));
|
||||
}
|
||||
|
||||
static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset,
|
||||
int input)
|
||||
{
|
||||
struct ti_ads7950_state *st = gpiochip_get_data(chip);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&st->slock);
|
||||
|
||||
/* Only change direction if needed */
|
||||
if (input && (st->gpio_cmd_settings_bitmask & BIT(offset)))
|
||||
st->gpio_cmd_settings_bitmask &= ~BIT(offset);
|
||||
else if (!input && !(st->gpio_cmd_settings_bitmask & BIT(offset)))
|
||||
st->gpio_cmd_settings_bitmask |= BIT(offset);
|
||||
else
|
||||
goto out;
|
||||
|
||||
st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
|
||||
out:
|
||||
mutex_unlock(&st->slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_ads7950_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
return _ti_ads7950_set_direction(chip, offset, 1);
|
||||
}
|
||||
|
||||
static int ti_ads7950_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
ti_ads7950_set(chip, offset, value);
|
||||
|
||||
return _ti_ads7950_set_direction(chip, offset, 0);
|
||||
}
|
||||
|
||||
static int ti_ads7950_init_hw(struct ti_ads7950_state *st)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&st->slock);
|
||||
|
||||
/* Settings for Manual/Auto1/Auto2 commands */
|
||||
/* Default to 5v ref */
|
||||
st->cmd_settings_bitmask = TI_ADS7950_CR_RANGE_5V;
|
||||
st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Settings for GPIO command */
|
||||
st->gpio_cmd_settings_bitmask = 0x0;
|
||||
st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
|
||||
out:
|
||||
mutex_unlock(&st->slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_ads7950_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ti_ads7950_state *st;
|
||||
|
@ -386,7 +553,6 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
|||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->settings = TI_ADS7950_CR_MANUAL | TI_ADS7950_CR_RANGE_5V;
|
||||
|
||||
info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
|
@ -432,16 +598,19 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
|||
if (ACPI_COMPANION(&spi->dev))
|
||||
st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT;
|
||||
|
||||
mutex_init(&st->slock);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
|
||||
return PTR_ERR(st->reg);
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto error_destroy_mutex;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
|
||||
return ret;
|
||||
goto error_destroy_mutex;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
|
@ -451,18 +620,46 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
|||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = ti_ads7950_init_hw(st);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to init adc chip\n");
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device\n");
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
|
||||
/* Add GPIO chip */
|
||||
st->chip.label = dev_name(&st->spi->dev);
|
||||
st->chip.parent = &st->spi->dev;
|
||||
st->chip.owner = THIS_MODULE;
|
||||
st->chip.base = -1;
|
||||
st->chip.ngpio = TI_ADS7950_NUM_GPIOS;
|
||||
st->chip.get_direction = ti_ads7950_get_direction;
|
||||
st->chip.direction_input = ti_ads7950_direction_input;
|
||||
st->chip.direction_output = ti_ads7950_direction_output;
|
||||
st->chip.get = ti_ads7950_get;
|
||||
st->chip.set = ti_ads7950_set;
|
||||
|
||||
ret = gpiochip_add_data(&st->chip, st);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to init GPIOs\n");
|
||||
goto error_iio_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_iio_device:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
error_destroy_mutex:
|
||||
mutex_destroy(&st->slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -472,9 +669,11 @@ static int ti_ads7950_remove(struct spi_device *spi)
|
|||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
|
||||
gpiochip_remove(&st->chip);
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
mutex_destroy(&st->slock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADS8344 16-bit 8-Channel ADC driver
|
||||
*
|
||||
* Author: Gregory CLEMENT <gregory.clement@bootlin.com>
|
||||
*
|
||||
* Datasheet: http://www.ti.com/lit/ds/symlink/ads8344.pdf
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define ADS8344_START BIT(7)
|
||||
#define ADS8344_SINGLE_END BIT(2)
|
||||
#define ADS8344_CHANNEL(channel) ((channel) << 4)
|
||||
#define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */
|
||||
|
||||
struct ads8344 {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
/*
|
||||
* Lock protecting access to adc->tx_buff and rx_buff,
|
||||
* especially from concurrent read on sysfs file.
|
||||
*/
|
||||
struct mutex lock;
|
||||
|
||||
u8 tx_buf ____cacheline_aligned;
|
||||
u16 rx_buf;
|
||||
};
|
||||
|
||||
#define ADS8344_VOLTAGE_CHANNEL(chan, si) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan1), \
|
||||
.channel2 = (chan2), \
|
||||
.differential = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ads8344_channels[] = {
|
||||
ADS8344_VOLTAGE_CHANNEL(0, 0),
|
||||
ADS8344_VOLTAGE_CHANNEL(1, 4),
|
||||
ADS8344_VOLTAGE_CHANNEL(2, 1),
|
||||
ADS8344_VOLTAGE_CHANNEL(3, 5),
|
||||
ADS8344_VOLTAGE_CHANNEL(4, 2),
|
||||
ADS8344_VOLTAGE_CHANNEL(5, 6),
|
||||
ADS8344_VOLTAGE_CHANNEL(6, 3),
|
||||
ADS8344_VOLTAGE_CHANNEL(7, 7),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14),
|
||||
ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
|
||||
};
|
||||
|
||||
static int ads8344_adc_conversion(struct ads8344 *adc, int channel,
|
||||
bool differential)
|
||||
{
|
||||
struct spi_device *spi = adc->spi;
|
||||
int ret;
|
||||
|
||||
adc->tx_buf = ADS8344_START;
|
||||
if (!differential)
|
||||
adc->tx_buf |= ADS8344_SINGLE_END;
|
||||
adc->tx_buf |= ADS8344_CHANNEL(channel);
|
||||
adc->tx_buf |= ADS8344_CLOCK_INTERNAL;
|
||||
|
||||
ret = spi_write(spi, &adc->tx_buf, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(9);
|
||||
|
||||
ret = spi_read(spi, &adc->rx_buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adc->rx_buf;
|
||||
}
|
||||
|
||||
static int ads8344_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *value,
|
||||
int *shift, long mask)
|
||||
{
|
||||
struct ads8344 *adc = iio_priv(iio);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
*value = ads8344_adc_conversion(adc, channel->scan_index,
|
||||
channel->differential);
|
||||
mutex_unlock(&adc->lock);
|
||||
if (*value < 0)
|
||||
return *value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*value = regulator_get_voltage(adc->reg);
|
||||
if (*value < 0)
|
||||
return *value;
|
||||
|
||||
/* convert regulator output voltage to mV */
|
||||
*value /= 1000;
|
||||
*shift = 16;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ads8344_info = {
|
||||
.read_raw = ads8344_read_raw,
|
||||
};
|
||||
|
||||
static int ads8344_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ads8344 *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->name = dev_name(&spi->dev);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->info = &ads8344_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ads8344_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads8344_channels);
|
||||
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg))
|
||||
return PTR_ERR(adc->reg);
|
||||
|
||||
ret = regulator_enable(adc->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
regulator_disable(adc->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads8344_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ads8344 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ads8344_of_match[] = {
|
||||
{ .compatible = "ti,ads8344", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ads8344_of_match);
|
||||
|
||||
static struct spi_driver ads8344_driver = {
|
||||
.driver = {
|
||||
.name = "ads8344",
|
||||
.of_match_table = ads8344_of_match,
|
||||
},
|
||||
.probe = ads8344_probe,
|
||||
.remove = ads8344_remove,
|
||||
};
|
||||
module_spi_driver(ads8344_driver);
|
||||
|
||||
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@bootlin.com>");
|
||||
MODULE_DESCRIPTION("ADS8344 driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -523,6 +523,6 @@ static struct spi_driver ads8688_driver = {
|
|||
};
|
||||
module_spi_driver(ads8688_driver);
|
||||
|
||||
MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
|
||||
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADS8688 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -36,7 +36,8 @@ static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data)
|
|||
static void iio_buffer_cb_release(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
|
||||
kfree(cb_buff->buffer.scan_mask);
|
||||
|
||||
bitmap_free(cb_buff->buffer.scan_mask);
|
||||
kfree(cb_buff);
|
||||
}
|
||||
|
||||
|
@ -74,9 +75,8 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
}
|
||||
|
||||
cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
|
||||
cb_buff->buffer.scan_mask
|
||||
= kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
cb_buff->buffer.scan_mask = bitmap_zalloc(cb_buff->indio_dev->masklength,
|
||||
GFP_KERNEL);
|
||||
if (cb_buff->buffer.scan_mask == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
|
@ -95,7 +95,7 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
return cb_buff;
|
||||
|
||||
error_free_scan_mask:
|
||||
kfree(cb_buff->buffer.scan_mask);
|
||||
bitmap_free(cb_buff->buffer.scan_mask);
|
||||
error_release_channels:
|
||||
iio_channel_release_all(cb_buff->channels);
|
||||
error_free_cb_buff:
|
||||
|
|
|
@ -12,14 +12,14 @@ config ATLAS_PH_SENSOR
|
|||
select IIO_TRIGGERED_BUFFER
|
||||
select IRQ_WORK
|
||||
help
|
||||
Say Y here to build I2C interface support for the following
|
||||
Atlas Scientific OEM SM sensors:
|
||||
Say Y here to build I2C interface support for the following
|
||||
Atlas Scientific OEM SM sensors:
|
||||
* pH SM sensor
|
||||
* EC SM sensor
|
||||
* ORP SM sensor
|
||||
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
||||
config BME680
|
||||
tristate "Bosch Sensortec BME680 sensor driver"
|
||||
|
@ -47,8 +47,8 @@ config BME680_SPI
|
|||
config CCS811
|
||||
tristate "AMS CCS811 VOC sensor"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here to build I2C interface support for the AMS
|
||||
CCS811 VOC (Volatile Organic Compounds) sensor
|
||||
|
|
|
@ -321,7 +321,12 @@ static int pms7003_probe(struct serdev_device *serdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id pms7003_of_match[] = {
|
||||
{ .compatible = "plantower,pms1003" },
|
||||
{ .compatible = "plantower,pms3003" },
|
||||
{ .compatible = "plantower,pms5003" },
|
||||
{ .compatible = "plantower,pms6003" },
|
||||
{ .compatible = "plantower,pms7003" },
|
||||
{ .compatible = "plantower,pmsa003" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pms7003_of_match);
|
||||
|
|
|
@ -1,22 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
|
||||
*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This driver uses the cros-ec interface to communicate with the Chrome OS
|
||||
* EC about sensors data. Data access is presented through iio sysfs.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
|
@ -30,7 +21,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#define CROS_EC_SENSORS_MAX_CHANNELS 4
|
||||
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
|
||||
*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
@ -25,7 +17,6 @@
|
|||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static char *cros_ec_loc[] = {
|
||||
|
@ -269,6 +260,17 @@ static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_read_lpc() - read acceleration data from EC shared memory.
|
||||
* @indio_dev: pointer to IIO device.
|
||||
* @scan_mask: bitmap of the sensor indices to scan.
|
||||
* @data: location to store data.
|
||||
*
|
||||
* Note: this is the safe function for reading the EC data. It guarantees
|
||||
* that the data sampled was not modified by the EC while being read.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure.
|
||||
*/
|
||||
int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
#
|
||||
|
||||
config IIO_MS_SENSORS_I2C
|
||||
tristate
|
||||
tristate
|
||||
|
|
|
@ -81,7 +81,7 @@ int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
|
|||
unsigned int len, int64_t timestamp)
|
||||
{
|
||||
__le32 time;
|
||||
int64_t calculated_time;
|
||||
int64_t calculated_time = 0;
|
||||
struct ssp_sensor_data *spd = iio_priv(indio_dev);
|
||||
|
||||
if (indio_dev->scan_bytes == 0)
|
||||
|
|
|
@ -1,631 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
#define QUAD8_REG_CHAN_OP 0x11
|
||||
#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
|
||||
/* Borrow Toggle flip-flop */
|
||||
#define QUAD8_FLAG_BT BIT(0)
|
||||
/* Carry Toggle flip-flop */
|
||||
#define QUAD8_FLAG_CT BIT(1)
|
||||
/* Error flag */
|
||||
#define QUAD8_FLAG_E BIT(4)
|
||||
/* Up/Down flag */
|
||||
#define QUAD8_FLAG_UD BIT(5)
|
||||
/* Reset and Load Signal Decoders */
|
||||
#define QUAD8_CTR_RLD 0x00
|
||||
/* Counter Mode Register */
|
||||
#define QUAD8_CTR_CMR 0x20
|
||||
/* Input / Output Control Register */
|
||||
#define QUAD8_CTR_IOR 0x40
|
||||
/* Index Control Register */
|
||||
#define QUAD8_CTR_IDR 0x60
|
||||
/* Reset Byte Pointer (three byte data pointer) */
|
||||
#define QUAD8_RLD_RESET_BP 0x01
|
||||
/* Reset Counter */
|
||||
#define QUAD8_RLD_RESET_CNTR 0x02
|
||||
/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
|
||||
#define QUAD8_RLD_RESET_FLAGS 0x04
|
||||
/* Reset Error flag */
|
||||
#define QUAD8_RLD_RESET_E 0x06
|
||||
/* Preset Register to Counter */
|
||||
#define QUAD8_RLD_PRESET_CNTR 0x08
|
||||
/* Transfer Counter to Output Latch */
|
||||
#define QUAD8_RLD_CNTR_OUT 0x10
|
||||
#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
|
||||
#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
|
||||
|
||||
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 + QUAD8_REG_INDEX_INPUT_LEVELS)
|
||||
& BIT(chan->channel));
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
flags = inb(base_offset + 1);
|
||||
borrow = flags & QUAD8_FLAG_BT;
|
||||
carry = !!(flags & QUAD8_FLAG_CT);
|
||||
|
||||
/* Borrow XOR Carry effectively doubles count range */
|
||||
*val = (borrow ^ carry) << 24;
|
||||
|
||||
/* Reset Byte Pointer; transfer Counter to Output Latch */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
|
||||
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(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, 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(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, 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(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
|
||||
/* Reset Error flag */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, 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(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
|
||||
|
||||
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 = {
|
||||
.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(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, 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 + 1;
|
||||
bool preset_enable;
|
||||
int ret;
|
||||
unsigned int ior_cfg;
|
||||
|
||||
ret = kstrtobool(buf, &preset_enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Preset enable is active low in Input/Output Control register */
|
||||
preset_enable = !preset_enable;
|
||||
|
||||
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(QUAD8_CTR_IOR | 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) & QUAD8_FLAG_E);
|
||||
}
|
||||
|
||||
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) & QUAD8_FLAG_UD);
|
||||
}
|
||||
|
||||
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(QUAD8_CTR_CMR | 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(QUAD8_CTR_IDR | 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(QUAD8_CTR_CMR | 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(QUAD8_CTR_IDR | 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);
|
||||
indio_dev->dev.parent = dev;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
||||
/* Reset all counters and disable interrupt function */
|
||||
outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
|
||||
/* Set initial configuration for all counters */
|
||||
for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
|
||||
base_offset = base[id] + 2 * i;
|
||||
/* Reset Byte Pointer */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
/* Reset Preset Register */
|
||||
for (j = 0; j < 3; j++)
|
||||
outb(0x00, base_offset);
|
||||
/* Reset Borrow, Carry, Compare, and Sign flags */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
|
||||
/* Reset Error flag */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
|
||||
/* Binary encoding; Normal count; non-quadrature mode */
|
||||
outb(QUAD8_CTR_CMR, base_offset + 1);
|
||||
/* Disable A and B inputs; preset on index; FLG1 as Carry */
|
||||
outb(QUAD8_CTR_IOR, base_offset + 1);
|
||||
/* Disable index function; negative index polarity */
|
||||
outb(QUAD8_CTR_IDR, base_offset + 1);
|
||||
}
|
||||
/* Enable all counters */
|
||||
outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
|
||||
|
||||
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");
|
|
@ -1,34 +0,0 @@
|
|||
#
|
||||
# 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 PC104 && X86
|
||||
select 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.
|
||||
|
||||
config STM32_LPTIMER_CNT
|
||||
tristate "STM32 LP Timer encoder counter driver"
|
||||
depends on MFD_STM32_LPTIMER || COMPILE_TEST
|
||||
help
|
||||
Select this option to enable STM32 Low-Power Timer quadrature encoder
|
||||
and counter driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stm32-lptimer-cnt.
|
||||
endmenu
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# Makefile for IIO counter devices
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
|
||||
obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
|
|
@ -1,382 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* STM32 Low-Power Timer Encoder and Counter driver
|
||||
*
|
||||
* Copyright (C) STMicroelectronics 2017
|
||||
*
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>
|
||||
*
|
||||
* Inspired by 104-quad-8 and stm32-timer-trigger drivers.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/mfd/stm32-lptimer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct stm32_lptim_cnt {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
u32 preset;
|
||||
u32 polarity;
|
||||
u32 quadrature_mode;
|
||||
};
|
||||
|
||||
static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return FIELD_GET(STM32_LPTIM_ENABLE, val);
|
||||
}
|
||||
|
||||
static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
|
||||
int enable)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!enable) {
|
||||
clk_disable(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LP timer must be enabled before writing CMP & ARR */
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ensure CMP & ARR registers are properly written */
|
||||
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
|
||||
(val & STM32_LPTIM_CMPOK_ARROK),
|
||||
100, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
|
||||
STM32_LPTIM_CMPOKCF_ARROKCF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret) {
|
||||
regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Start LP timer in continuous mode */
|
||||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
|
||||
STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
|
||||
}
|
||||
|
||||
static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
|
||||
{
|
||||
u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
|
||||
STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
|
||||
u32 val;
|
||||
|
||||
/* Setup LP timer encoder/counter and polarity, without prescaler */
|
||||
if (priv->quadrature_mode)
|
||||
val = enable ? STM32_LPTIM_ENC : 0;
|
||||
else
|
||||
val = enable ? STM32_LPTIM_COUNTMODE : 0;
|
||||
val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
|
||||
|
||||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
|
||||
}
|
||||
|
||||
static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
if (val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check nobody uses the timer, or already disabled/enabled */
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if ((ret < 0) || (!ret && !val))
|
||||
return ret;
|
||||
if (val && ret)
|
||||
return -EBUSY;
|
||||
|
||||
ret = stm32_lptim_setup(priv, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return stm32_lptim_set_enable_state(priv, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
u32 dat;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = dat;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Non-quadrature mode: scale = 1 */
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
if (priv->quadrature_mode) {
|
||||
/*
|
||||
* Quadrature encoder mode:
|
||||
* - both edges, quarter cycle, scale is 0.25
|
||||
* - either rising/falling edge scale is 0.5
|
||||
*/
|
||||
if (priv->polarity > 1)
|
||||
*val2 = 2;
|
||||
else
|
||||
*val2 = 1;
|
||||
}
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info stm32_lptim_cnt_iio_info = {
|
||||
.read_raw = stm32_lptim_read_raw,
|
||||
.write_raw = stm32_lptim_write_raw,
|
||||
};
|
||||
|
||||
static const char *const stm32_lptim_quadrature_modes[] = {
|
||||
"non-quadrature",
|
||||
"quadrature",
|
||||
};
|
||||
|
||||
static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->quadrature_mode;
|
||||
}
|
||||
|
||||
static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
priv->quadrature_mode = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum stm32_lptim_quadrature_mode_en = {
|
||||
.items = stm32_lptim_quadrature_modes,
|
||||
.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
|
||||
.get = stm32_lptim_get_quadrature_mode,
|
||||
.set = stm32_lptim_set_quadrature_mode,
|
||||
};
|
||||
|
||||
static const char * const stm32_lptim_cnt_polarity[] = {
|
||||
"rising-edge", "falling-edge", "both-edges",
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return priv->polarity;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
priv->polarity = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum stm32_lptim_cnt_polarity_en = {
|
||||
.items = stm32_lptim_cnt_polarity,
|
||||
.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
|
||||
.get = stm32_lptim_cnt_get_polarity,
|
||||
.set = stm32_lptim_cnt_set_polarity,
|
||||
};
|
||||
|
||||
static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
ret = kstrtouint(buf, 0, &priv->preset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->preset > STM32_LPTIM_MAX_ARR)
|
||||
return -EINVAL;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* LP timer with encoder */
|
||||
static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
|
||||
{
|
||||
.name = "preset",
|
||||
.shared = IIO_SEPARATE,
|
||||
.read = stm32_lptim_cnt_get_preset,
|
||||
.write = stm32_lptim_cnt_set_preset,
|
||||
},
|
||||
IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
|
||||
IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
|
||||
IIO_ENUM("quadrature_mode", IIO_SEPARATE,
|
||||
&stm32_lptim_quadrature_mode_en),
|
||||
IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec stm32_lptim_enc_channels = {
|
||||
.type = IIO_COUNT,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_ENABLE) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = stm32_lptim_enc_ext_info,
|
||||
.indexed = 1,
|
||||
};
|
||||
|
||||
/* LP timer without encoder (counter only) */
|
||||
static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
|
||||
{
|
||||
.name = "preset",
|
||||
.shared = IIO_SEPARATE,
|
||||
.read = stm32_lptim_cnt_get_preset,
|
||||
.write = stm32_lptim_cnt_set_preset,
|
||||
},
|
||||
IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
|
||||
IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec stm32_lptim_cnt_channels = {
|
||||
.type = IIO_COUNT,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_ENABLE) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = stm32_lptim_cnt_ext_info,
|
||||
.indexed = 1,
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct stm32_lptim_cnt *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (IS_ERR_OR_NULL(ddata))
|
||||
return -EINVAL;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->dev = &pdev->dev;
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->preset = STM32_LPTIM_MAX_ARR;
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &stm32_lptim_cnt_iio_info;
|
||||
if (ddata->has_encoder)
|
||||
indio_dev->channels = &stm32_lptim_enc_channels;
|
||||
else
|
||||
indio_dev->channels = &stm32_lptim_cnt_channels;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_lptim_cnt_of_match[] = {
|
||||
{ .compatible = "st,stm32-lptimer-counter", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
|
||||
|
||||
static struct platform_driver stm32_lptim_cnt_driver = {
|
||||
.probe = stm32_lptim_cnt_probe,
|
||||
.driver = {
|
||||
.name = "stm32-lptimer-counter",
|
||||
.of_match_table = stm32_lptim_cnt_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_lptim_cnt_driver);
|
||||
|
||||
MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
|
||||
MODULE_ALIAS("platform:stm32-lptimer-counter");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -112,6 +112,8 @@ struct ad5064_state {
|
|||
bool use_internal_vref;
|
||||
|
||||
ad5064_write_func write;
|
||||
/* Lock used to maintain consistency between cached and dev state */
|
||||
struct mutex lock;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
|
@ -248,11 +250,11 @@ static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev,
|
|||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
st->pwr_down_mode[chan->channel] = mode + 1;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, chan);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -291,11 +293,11 @@ static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
st->pwr_down[chan->channel] = pwr_down;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, chan);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
|
@ -349,12 +351,12 @@ static int ad5064_write_raw(struct iio_dev *indio_dev,
|
|||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->channel] = val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -856,6 +858,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,
|
|||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->chip_info = &ad5064_chip_info_tbl[type];
|
||||
|
|
|
@ -72,8 +72,6 @@
|
|||
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0)
|
||||
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5)
|
||||
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5)
|
||||
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK BIT(7)
|
||||
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(x) (((x) & 0x1) << 7)
|
||||
|
||||
/* AD5758_DCDC_CONFIG2 */
|
||||
#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1)
|
||||
|
@ -84,6 +82,10 @@
|
|||
/* AD5758_DIGITAL_DIAG_RESULTS */
|
||||
#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15)
|
||||
|
||||
/* AD5758_ADC_CONFIG */
|
||||
#define AD5758_ADC_CONFIG_PPC_BUF_EN(x) (((x) & 0x1) << 11)
|
||||
#define AD5758_ADC_CONFIG_PPC_BUF_MSK BIT(11)
|
||||
|
||||
#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F))
|
||||
|
||||
#define AD5758_FULL_SCALE_MICRO 65535000000ULL
|
||||
|
@ -315,6 +317,18 @@ static int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st,
|
|||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The ENABLE_PPC_BUFFERS bit must be set prior to enabling PPC current
|
||||
* mode.
|
||||
*/
|
||||
if (mode == AD5758_DCDC_MODE_PPC_CURRENT) {
|
||||
ret = ad5758_spi_write_mask(st, AD5758_ADC_CONFIG,
|
||||
AD5758_ADC_CONFIG_PPC_BUF_MSK,
|
||||
AD5758_ADC_CONFIG_PPC_BUF_EN(1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
|
||||
AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
|
||||
AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
|
||||
|
@ -444,23 +458,6 @@ static int ad5758_set_out_range(struct ad5758_state *st, int range)
|
|||
AD5758_CAL_MEM_UNREFRESHED_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_fault_prot_switch_en(struct ad5758_state *st, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK,
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(enable));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
|
||||
* This allows the 3-wire interface communication to complete.
|
||||
*/
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
|
||||
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
@ -585,8 +582,8 @@ static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
unsigned int dcdc_config1_mode, dc_dc_mode, dac_config_mode, val;
|
||||
unsigned long int dcdc_config1_msk, dac_config_msk;
|
||||
unsigned int dc_dc_mode, dac_config_mode, val;
|
||||
unsigned long int dac_config_msk;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &pwr_down);
|
||||
|
@ -602,17 +599,6 @@ static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
|
|||
val = 1;
|
||||
}
|
||||
|
||||
dcdc_config1_mode = AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(dc_dc_mode) |
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(val);
|
||||
dcdc_config1_msk = AD5758_DCDC_CONFIG1_DCDC_MODE_MSK |
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
|
||||
dcdc_config1_msk,
|
||||
dcdc_config1_mode);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
|
||||
AD5758_DAC_CONFIG_INT_EN_MODE(val);
|
||||
dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK |
|
||||
|
@ -841,11 +827,6 @@ static int ad5758_init(struct ad5758_state *st)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Enable the VIOUT fault protection switch (FPS is closed) */
|
||||
ret = ad5758_fault_prot_switch_en(st, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Power up the DAC and internal (INT) amplifiers */
|
||||
ret = ad5758_internal_buffers_en(st, 1);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -429,6 +429,6 @@ static struct i2c_driver dac5571_driver = {
|
|||
};
|
||||
module_i2c_driver(dac5571_driver);
|
||||
|
||||
MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
|
||||
MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
|
||||
MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -196,7 +196,10 @@ static __init int iio_dummy_evgen_init(void)
|
|||
return ret;
|
||||
device_initialize(&iio_evgen_dev);
|
||||
dev_set_name(&iio_evgen_dev, "iio_evgen");
|
||||
return device_add(&iio_evgen_dev);
|
||||
ret = device_add(&iio_evgen_dev);
|
||||
if (ret)
|
||||
put_device(&iio_evgen_dev);
|
||||
return ret;
|
||||
}
|
||||
module_init(iio_dummy_evgen_init);
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче