1
0
Fork 0

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
  ...
hifive-unleashed-5.2
Linus Torvalds 2019-05-07 13:31:29 -07:00
commit e0dccbdf5a
927 changed files with 22746 additions and 134389 deletions

View File

@ -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>

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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).

View File

@ -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";
};

View File

@ -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>;
};
};

View File

@ -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>;
};
};

View File

@ -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 */
};
};
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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>;
};

View File

@ -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

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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:

View File

@ -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)>;
};

View File

@ -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 = <&reg_peri_3p15v>;
vddio-supply = <&reg_peri_3p15v>;
};

View File

@ -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";
};

View File

@ -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:

View File

@ -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>;
};

View File

@ -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>;
};

Before

Width:  |  Height:  |  Size: 745 B

View File

@ -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>;
};
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -49,6 +49,7 @@ Accelerometers:
- st,lis2dw12
- st,lis3dhh
- st,lis3de
- st,lis2de12
Gyroscopes:
- st,l3g4200d-gyro

View File

@ -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>;
};

View File

@ -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.

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -57,6 +57,7 @@ available subsections can be seen below.
soundwire/index
fpga/index
acpi/index
generic-counter
.. only:: subproject and html

View File

@ -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)

View File

@ -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>;

View File

@ -230,4 +230,6 @@ source "drivers/slimbus/Kconfig"
source "drivers/interconnect/Kconfig"
source "drivers/counter/Kconfig"
endmenu

View File

@ -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/

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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"

View File

@ -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/

View File

@ -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:

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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]);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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)) {

View File

@ -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);

View File

@ -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,
},
{},
};

View File

@ -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);

View File

@ -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)
{

View File

@ -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,

View File

@ -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,
},
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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");

View File

@ -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");

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -3,4 +3,4 @@
#
config IIO_MS_SENSORS_I2C
tristate
tristate

View File

@ -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)

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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];

View File

@ -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)

View File

@ -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");

View File

@ -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);

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