Skip to content

Conversation

Rubusch
Copy link

@Rubusch Rubusch commented Oct 13, 2025

TL;DR

I tried to refac this driver, fix some of the FIFO related issues and introduce an approach to cover trigger/fetch&get and stream/RTIO bus access.

Long story

In my opinion, the ADXL345 driver in Zephyr still has room for improvement. Having worked on parts of the ADXL345 driver for the Linux kernel, I found it a familiar starting point when getting into Zephyr.

Current State of the ADXL345 Driver in Zephyr:

FIFO Mode Configuration

The driver currently (at least) offers setting up Analog Devices' FIFO_TRIGGER mode. However, this appears to be a misunderstanding of its purpose, especially when viewed in the context of Zephyr’s Trigger API versus a Stream/RTIO-based approach. It's important to note that Analog Devices did not define the FIFO_TRIGGER bit with Zephyr’s trigger model in mind - Analog's FIFO_TRIGGER serves a different function entirely. For typical usage in Zephyr, starting with either FIFO_STREAM or FIFO_BYPASSED modes would be more appropriate, in my opinion.

Broken FIFO Handling

FIFO functionality has been broken for several months and remains so. Currently, a FIFO watermark event is triggered once, but the driver only fetches the most recent measurement(s), rather than reading out the entire FIFO buffer. As a result, no further watermark interrupts are generated after the initial one, since the FIFO is never properly emptied.

Additional Issues

The FIFO watermark should ideally trigger at 2 stored samples, not just 1.

The current FIFO flush mechanism relies on the process described for FIFO_TRIGGER mode in the datasheet. However, when using FIFO_STREAM mode - as recommended - the FIFO should be flushed by reading out all entries manually, as per the datasheet.

Initialization in stream/RTIO mode uses direct bus writes, which makes later fetch/get operations or RTIO usage somewhat redundant. A better approach would be to consolidate these accesses under shared functions that abstract the bus type (RTIO or plain bus).

For register updates where only a few bits need changing, using an _update_bits() function with cached register values could reduce bus traffic. Since configuration registers are not expected to change externally, this kind of caching (similar to the regmap cache mechanism in Linux) could be beneficial. I'm not sure if Zephyr has a general caching mechanism like this, but it could be worth considering.

General Refactoring Needed

Overall, the driver would benefit from a thorough refactoring. There are inconsistencies in naming, instances of dead code, and unnecessarily complex implementations - likely due to the driver evolving without consistent maintenance. In my view, many of these parts are no longer robust or maintainable and need cleanup.

@MaureenHelm : I had a chat with you as the Analog Devices maintainer during OSS in Amsterdam this year. I'm not sure if you remember, anyway you had suggested that I first submit the full series as-is, so that we could later decide how to proceed—whether to split, squash, accept, or drop parts of it. Since it's generally easier to squash commits than to separate them after the fact, I tried to break the series down as much as possible from the beginning. That’s why you're now seeing over 40 commits. I’m also aware that some maintainers prefer this level of granularity.

Please let me know if this approach is excessive or if it's aligned with your expectations. All commits build successfully and pass strict checkpatch. For verification, I used my own test applications for fetch&get, device handler registration, and stream/RTIO integration, as the existing samples didn’t provide enough coverage in those areas. Does the set - all or in pieces - qualify for upstreaming? What should be squashed? What is missing? What needs to be improved? Actually I pretend writing drivers for similar accelerometers by this kind of approach. So, this is kind of a proposal to get a bit into discussion, too.

In a summary of the main topics of this series, I'd like to address:

  • refac: comments, simplify variable names, adjust variable types, removal of dead code, remove redundant code

  • refac: control measurement on/off by dedicate function and mech

  • refac: separate function get_status() into get_fifo_entries() and get_status() for interrupt status

  • refac: introduce an api to use common function calls diverting to use underlying either triggered/fetch&get or stream/RTIO implementation

  • refac: make interrupt lines configurable over devicetree, and remove rechecking over and over again in the running code for such constant setting

  • refac: simplify overly complicated ODR setup, as then also g ranges

  • refac: stream handler registration

  • fix: change minimum watermark to 2

  • fix: stream initialization using direct register write, while partly using RTIO

  • fix: stream flush FIFO performs the action documented for reset Analog Device's trigger mode (switch to FIFO BYPASS and back), in Analyg Device's FIFO STREAM mode, entries need actually to be read, to reset fifo status register and interrupt status register (would come much more into play when implementing sensor events)

  • fix: fetch&get using FIFO did actually fetch the recent measurements, but did not read out fifo entries

  • fix: rework FIFO mode setup - Analog Device's "FIFO TRIGGER" mode seems to be confused with Zephyr's "triggered" config?

  • fix/add: let FIFO watermark being configured also dynamically by attribute for fetch&get

  • fix/add: overrun and data ready handler, optionally to be overwritten in application

NB: I used AI to elaborate code comments and improve the English descriptions, simply because I'm not a native speaker. I did not use AI / Copilots or the like for sources.

Add a dedicated devicetree binding to configure the interrupt line, INT1 or
INT2, used for FIFO events. FIFO events can be data ready, watermark or
overrun.

Signed-off-by: Lothar Rubusch <[email protected]>
The FIFO is used either to buffer values in Analog's "stream mode" or to
record a set of measurements triggered by a specific sensor event in
Analog's "trigger mode". Since sensor event handling is not yet implemented
in this driver, only stream mode is applicable. In both cases - especially
in stream mode - setting the watermark to 1 is not practical. Doing so
would generate an interrupt for every single measured value. If capturing
every measurement in real time is the goal, Analog's "bypass mode" is more
appropriate, as it avoids the additional overhead of handling interrupts
like watermark or data ready. Moreover, generating frequent interrupts can
easily cause overrun issues.

Therefore, the minimum configurable watermark should be changed to 2.

Signed-off-by: Lothar Rubusch <[email protected]>
It does not make sense to prioritize one interrupt line over the other on
the ADXL345. Once interrupt events are implemented, each event can be
individually mapped to either INT1 or INT2. In such configurations, both
interrupt lines must be physically connected, and the mapping should be
defined accordingly.

Remove that comment line.

Signed-off-by: Lothar Rubusch <[email protected]>
Adjustment in the driver according to the minimum change for the
devicetree.

Signed-off-by: Lothar Rubusch <[email protected]>
Use more consistent naming conventions. A significant number of the current
defines in this driver are unused. Clarify that this define refers to a
configuration register by adding the _REG suffix to its name.

Signed-off-by: Lothar Rubusch <[email protected]>
Use more consistent naming conventions. A significant number of the current
defines in this driver are unused. Clarify that this define refers to a
configuration register by adding the _REG suffix to its name.

Signed-off-by: Lothar Rubusch <[email protected]>
Use more consistent naming conventions. A significant number of the current
defines in this driver are unused. Clarify that this define refers to a
configuration register by adding the _REG suffix to its name.

Signed-off-by: Lothar Rubusch <[email protected]>
The current name neither accurately represents its purpose nor indicates
its context when viewed in debug symbols. Therefore, prefix the define with
ADXL345_ and apply consistent naming for clarity.

Signed-off-by: Lothar Rubusch <[email protected]>
The current name neither accurately represents its purpose nor indicates
its context when viewed in debug symbols. Therefore, prefix the define with
ADXL345_ and apply consistent naming for clarity.

Signed-off-by: Lothar Rubusch <[email protected]>
Use a descriptive and complete name for this constant. It represents a
field within the FIFO_CTL register and is part of the ADXL345 sensor
driver.

Signed-off-by: Lothar Rubusch <[email protected]>
Since the variable is used as a boolean, it should be defined as a bool.

Signed-off-by: Lothar Rubusch <[email protected]>
The FIFO sample count is stored within a portion of an 8-bit register.
Since only 8-bit configuration registers are involved, it's better to use
uint8_t to avoid unnecessary mixing with uint16_t.

Signed-off-by: Lothar Rubusch <[email protected]>
The function is intended to update only the selected, i.e. masked, bits.
However, it doesn't currently behave as expected. Update it to function
correctly, following the typical behavior of a *_assign_bits() helper.

Signed-off-by: Lothar Rubusch <[email protected]>
Remove dead code.

Signed-off-by: Lothar Rubusch <[email protected]>
Remove dead code.

Signed-off-by: Lothar Rubusch <[email protected]>
Introduce a function to handle enabling and standby mode for measurements.
According to the datasheet, placing the sensor in standby is recommended
before applying certain configurations. This function will manage enabling
and disabling measurement in subsequent patches.

Introduce a helper function to conditionally set or clear specific masked
bits. This change prepares the codebase for upcoming patches in this
series.

Signed-off-by: Lothar Rubusch <[email protected]>
Currently, this function performs two distinct tasks: it retrieves both
"status" and "status1" i.e. the interrupt status and a field used to
indicate the number of FIFO entries. In upcoming patches, these two pieces
of information are not always required together. When only one is needed,
the current implementation forces the use of a dummy variable as a
workaround.

To improve clarity and efficiency, it's better to split get_status() into
two separate functions - one that retrieves the interrupt status (as the
name suggests), and another that retrieves the current number of entries
in the sensor's FIFO.

Signed-off-by: Lothar Rubusch <[email protected]>
…tree

The ADXL345 sensor provides two configurable interrupt lines that can be
mapped to various sensor events such as data ready, overrun, and watermark.
The previous hardcoded approach wiring only INT2, and the subsequent
quickfix using INT1 with a "routed_to_int2" boolean flag, may served as a
temporary solution.

However, dynamically checking what is essentially a soldered (or jumpered)
interrupt configuration at every function call introduces unnecessary
complexity and bus traffic. The use of ternary operators for determining
the active interrupt line also reduces code readability and consistency,
making the interrupt mapping logic easy to overlook.

This commit aims to implement support for the FIFO interrupt lines via the
devicetree drdy-pin and keeps this setting in the constant driver
configuration. Mapping shall happen only once at probing, thus does not
need to be rechecked either. Additionally, introduce generic functions to
enable interrupt lines and implements logic to determine which line is
currently in use.

This refactoring lays the groundwork for upcoming changes in this series.
While Zephyr’s trigger mode will compile with this commit, its runtime
behavior will be finalized in subsequent patches.

Signed-off-by: Lothar Rubusch <[email protected]>
Cosmetical change to simplify local variable name.

Signed-off-by: Lothar Rubusch <[email protected]>
Currently, the ODR supposed to be configured in the devicetree, then
at the start of the adxl345_init() re-written to hardcoded
ADXL345_RATE_25HZ, and at the end of the adxl345_init() set again to
data->odr. Why?!

Refactor sensor ODR initialization and define a reasonable default in case
devicetree configuration fails. In adxl345_init(), use the ODR value
already provided in the config to update the corresponding register,
ensuring that only the relevant field is modified.

Use FIELD_GET() on the ODR mask when setting the value, and write directly
to the register both during sensor initialization and within the attribute
function. This removes an unnecessary layer of indirection, allowing the
setter function to be dropped entirely.

Signed-off-by: Lothar Rubusch <[email protected]>
Refactor the configuration of the g-range settings. Group together the
setup of g-range selection, the full_res field, implicit clearing of the
justify bit, and interrupt inversion handling. Also refactor the
initialization probe function accordingly.

Signed-off-by: Lothar Rubusch <[email protected]>
Remove default values for FIFO mode, FIFO trigger and samples (the fifo
watermark) in the driver config.

Defaulting the fifo_watermark value to 0 is meaningless and potentially
problematic (see ADXL345 Data Sheet (Rev. G, p. 28: "undesired operation
may occur"), especially in a fixed configuration. In the best case, the
sensor would immediately trigger a watermark interrupt, effectively
disabling the FIFO buffer functionality.

FIFO mode is derrived from available interrupt and watermark (e.g.
drdy_pin) configuration. FIFO trigger is actually senseless, since it would
initialize the corresponding field in the ADXL345 register. This implements
Analog's trigger mode, which is not implemented here and most likely does
not make sense to be implemented.

Note: Analog trigger mode is not Zephyr trigger mode, pls. consult the
ADXL345 datasheet for the details.

Signed-off-by: Lothar Rubusch <[email protected]>
…_XYZ_REGS

Rename this register for better clarity. According to the datasheet, this
register corresponds to the first x-axis data byte, followed by the second
x-axis byte, then the y-axis, and finally the z-axis data in a similar
sequence. The sensor does not provide an option to selectively enable or
disable specific axes for FIFO usage. Consequently, all three axis data
must be fetched together for a single FIFO sample line.

By using this register as the starting point for the fetch, it is always
used to retrieve x-, y-, and z-axis samples in a single operation.
Therefore, renaming it to ADXL345_REG_DATA_XYZ_REGS would be more
appropriate and meaningful for the driver implementation.

Signed-off-by: Lothar Rubusch <[email protected]>
Remove the adxl345_interrupt_config() function entirely. Move the
initialization of interrupt-to-GPIO mapping directly into adxl345_init().
Eliminate the redundant reads of ADXL345_INT_MAP_REG and
ADXL345_INT_ENABLE_REG, as the function neither used the retrieved data nor
evaluated the return values.

Configure the FIFO mode based on the availability of interrupt lines,
selecting either FIFO_STREAM or FIFO_BYPASSED. Clean up the mode fields and
add clarifying comments.

Current FIFO setup behavior:
A) If Zephyr’s .config includes CONFIG_ADXL345_TRIGGER or STREAM set to
'y':
  * If INT1 or INT2 are defined in the devicetree: use FIFO_STREAM
  * If no interrupt lines are defined: use FIFO_BYPASSED

B) If Zephyr’s .config does not include CONFIG_ADXL345_TRIGGER or STREAM:
  * Interrupt lines are ignored: use FIFO_BYPASSED

Note that FIFO_STREAM, FIFO_BYPASSED, and FIFO_TRIGGER are hardware
operation modes of the Analog Devices ADXL345 sensor. They are unrelated to
Zephyr's "STREAM" or "TRIGGER" APIs. Past versions of this driver
incorrectly mixed these concepts (e.g. initializing a trigger interrupt
when FIFO_STREAM mode was set).

To implement FIFO_TRIGGER (where the sensor stores samples upon being
triggered) or FIFO_FIFO (an alternative retention mode), refer to the
ADXL345 datasheet. In most cases, STREAM and BYPASS modes are sufficient.

Lastly, note that reading from the FIFO queue in FIFO_STREAM mode is
currently broken in this driver; this issue is addressed in one of the
subsequent patches in this series.

Signed-off-by: Lothar Rubusch <[email protected]>
The FIFO configuration has to be configured based on the configuration but
also on the availability of interrupt lines. Since both interrupt lines are
available, this configuration cannot be done in the constant static
sensor config anymore.

Signed-off-by: Lothar Rubusch <[email protected]>
This event loop is unused and was likely copied from similar accelerometer
drivers.

Remove dead code.

Signed-off-by: Lothar Rubusch <[email protected]>
Remove unused defines and masks that reference fields in the interrupt
registers which are neither specified nor accessed anywhere in the code.

Signed-off-by: Lothar Rubusch <[email protected]>
For the simplified fetch-and-get implementation, clean up the trigger
source by removing the unnecessary re-mapping of interrupt lines left over
from the old trigger logic. Allow applications to register handlers for
data-ready, overrun, and watermark sensor events, as also activity.

Provide a mechanism for applications to register these handlers, and
alternatively offer default handling that clears and resets the FIFO after
an interrupt is received. Additionally, introduce a dedicated function to
flush and reset the FIFO.

Signed-off-by: Lothar Rubusch <[email protected]>
In Analog Devices' FIFO modes (e.g. STREAM), samples are stored in the
sensor's internal buffer, and a watermark interrupt signals when enough
samples are ready. The driver should then fetch all available samples in a
burst read.

Currently, the driver handles interrupts but reads only a single sample
each time, regardless of FIFO mode. This effectively reduces all modes
(FIFO, TRIGGER, STREAM) to BYPASS behavior. Additionally, fetch operations
only returned the last sample repeatedly instead of the full FIFO contents.

This commit restores proper FIFO operation by converting sample storage
back to an array, but fixing the fetch logic to read and return all
configured FIFO entries.

Signed-off-by: Lothar Rubusch <[email protected]>
Reorganize function order to simplify future refactoring. Structure the
source so that generic helper functions appear first, API-level functions
with RTIO calls and callbacks are placed in the middle, and consumer-facing
functions are positioned at the end.

Signed-off-by: Lothar Rubusch <[email protected]>
Clarify what is actually meant and handled-whether it refers to entries or
samples.

Remove any unnecessary handling of these variables. According to the
datasheet, fifo_entries represents the number of entries required to
trigger a watermark interrupt, while fifo_samples indicates the number of
samples collected.

Also, avoid using status1, since the ADXL345 has only a single interrupt
status field.

Signed-off-by: Lothar Rubusch <[email protected]>
Add a way for dynamic FIFO watermark configuration from the app when using
the memory-efficient fetch-and-get implementation. Implement this
consistently with the stream implementation using only the devicetree
configuration of fifo_watermark.

Correspondingly, update the memory configuration to allocate sufficient
space for the fifo_watermark number of samples in the RTIO/stream
implementation.

Signed-off-by: Lothar Rubusch <[email protected]>
Eliminate the use of status1 for the RTIO/stream implementation of this
driver.

Using status1 here is misleading, especially when it refers to a register
rather than a status field. Improve member variable naming for better
clarity and consistency.

Signed-off-by: Lothar Rubusch <[email protected]>
Introduce the function adxl345_sqe_done() in the RTIO/stream implementation
to improve and refactor error handling in the streaming component of the
ADXL345 driver.

Signed-off-by: Lothar Rubusch <[email protected]>
For both stream/RTIO and fetch-and-get, provide a generic bus access
interface. Implement simple caching of configuration registers to reduce
bus traffic, especially when updating specific bits.

This approach maintains a more generic driver implementation for common
functionality and avoids redundant reimplementations that depend solely on
either fetch-and-get or RTIO calls.

Additionally, to efficiently update register bits without reading
configuration registers that cannot be modified externally, maintain cached
copies of written settings and avoid reading their values from the bus
again.

Signed-off-by: Lothar Rubusch <[email protected]>
For the stream/RTIO implementation, add the function
adxl345_rtio_init_hdr() to initialize the stream driver header.

Also, add the function adxl345_rtio_init_buffer() to allocate the buffer
using rtio_sqe_rx_buf(), based on the initialized header.

Signed-off-by: Lothar Rubusch <[email protected]>
For the stream/RTIO implementation, add the function
adxl345_rtio_cqe_consume() to consume any remaining CQEs that have not yet
been processed.

Signed-off-by: Lothar Rubusch <[email protected]>
Introduce adxl345_check_streaming() in the stream/RTIO implementation to
handle the standard streaming check routines.

Signed-off-by: Lothar Rubusch <[email protected]>
Provide a function to flush the FIFO using both RTIO and fetch-and-get
mechanisms. In both cases, the FIFO must be flushed from Analog’s
FIFO_STREAM mode, which according to the datasheet, requires a complete
readout of all elements in order to reset both the interrupt source
register and the FIFO status register.

Currently, FIFO flushing is implemented by switching to FIFO_BYPASS mode
and then back again - the approach recommended by the datasheet for
FIFO_TRIGGER mode. However, Analog's FIFO_TRIGGER mode is a very specific
case and is typically not implemented in a generic ADXL345 drivers,
including this one. Switching over to FIFO_BYPASS might not reset the
interrupt register, when it comes to interrupt events.

Signed-off-by: Lothar Rubusch <[email protected]>
Extends the coverage of sensor events to overrun and watermark for the
stream/RTIO implementation.

Signed-off-by: Lothar Rubusch <[email protected]>
…tation

Restrict the RTIO stream handler to the kconfig condition.

Signed-off-by: Lothar Rubusch <[email protected]>
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants