Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions doc/reference/resource_management/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,107 @@ with onoff_monitor_register(). Notification of changes are provided
before issuing completion notifications associated with the new
state.

.. _resource_mgmt_sequence_manager:

Sequence Manager
****************

A sequence manager supports execution of a sequence of asynchronous actions.
User is notified when sequence is completed or when error occurs. Example
application are chain of I2C or SPI transfers.

A sequence is not modified by the manager thus if possible it can be constant.

The manager has the following properties:

* Manager can be in idle or active state. New sequence can be scheduled only if
manager is in idle state. Error is returned otherwise.
* Manager assumes that actions are asynchronous and next action is triggered
from the context in which previous action is completed.
* There are optional setup and teardown functions.
* Two modes are supported:
* Fixed processing function associated with the manager. Each action
contains specific data which is passed to the process function.
* Custom process function where each action contains dedicated processing
function followed by the data.
* Sequence is defined as an array of pointers thus each action data can have
different size.
* Sequence can be aborted. Aborted sequence finalizes on next action completion.
* On action finalization user can influence sequence execution by providing
offset which is taken into account when next item is picked. It can be used
to skip or repeat action based on action result.

Sequence manager provides helper macros for creating sequences and actions.
:c:macro:`SYS_SEQ_DEFINE` can be used to create and initialize sequence with
:c:macro:`SYS_SEQ_ACTION` or :c:macro:`SYS_SEQ_CUSTOM_ACTION` as arguments to
instantiate and initialize actions.

Additionally, manager provides generic actions which can be used in custom mode:

* :c:macro:`SYS_SEQ_ACTION_PAUSE` - pause action. On pause action execution,
provided handler is called. sys_seq_finalize must be called to resume sequence
execution.
* :c:macro:`SYS_SEQ_ACTION_MS_DELAY` and :c:macro:`SYS_SEQ_ACTION_US_DELAY` -
sequence execution is paused for requested amount of time. struct k_timer
instance provided in sys_seq_mgr_init is used to apply the delay.

.. doxygengroup:: resource_mgmt_onoff_apis
:project: Zephyr

.. _resource_mgmt_queued_operation:

Queued Operation Manager
************************

While :ref:`resource_mgmt_onoff` supports a shared resource that must be
available as long as any user still depends on it, the queued operation
manager provides serialized exclusive access to a resource that executes
operations asynchronously. This can be used to support (for example)
ADC sampling for different sensors, or groups of bus transactions.
Clients submit a operation request that is processed when the device
becomes available, with clients being notified of the completion of the
operation though the standard :ref:`async_notification`.

As with the on-off manager, the queued resource manager is a generic
infrastructure tool that should be used by a extending service, such as
an I2C bus controller or an ADC. The manager has the following
characteristics:

* The stable states are idle and processing. The manager always begins
in the idle state.
* The core client operations are submit (add an operation) and cancel
(remove an operation before it starts).
* Ownership of the operation object transitions from the client to the
manager when a queue request is accepted, and is returned to the
client when the manager notifies the client of operation completion.
* The core client event is completion. Manager state changes only as a
side effect from submitting or completing an operation.
* The service transitions from idle to processing when an operation is
submitted.
* The service transitions from processing to idle when notification of
the last operation has completed and there are no queued operations.
* The manager selects the next operation to process when notification of
completion has itself completed. In particular, changes to the set of
pending operations that are made during a completion callback affect
the next operation to execute.
* Each submitted operation includes a priority that orders execution by
first-come-first-served within priority.
* Operations are asynchronous, with completion notification through the
:ref:`async_notification`. The operations and notifications are run
in a context that is service-specific. This may be one or more
dedicated threads, or work queues. Notifications may come from
interrupt handlers. Note that operations may complete before the
submit request has returned to its caller.

The generic infrastructure holds the active operation and a queue of
pending operations. A service extension shall provide functions that:

* check that a request is well-formed, i.e. can be added to the queue;
* receive notification that a new operation is to be processed, or that
no operations are available (allowing the service to enter a
power-down mode);
* translate a generic completion callback into a service-specific
callback.

.. doxygengroup:: resource_mgmt_queued_operation_apis
:project: Zephyr
1 change: 1 addition & 0 deletions drivers/i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_GECKO i2c_gecko.c)
zephyr_library_sources_ifdef(CONFIG_I2C_RV32M1_LPI2C i2c_rv32m1_lpi2c.c)
zephyr_library_sources_ifdef(CONFIG_I2C_SAM0 i2c_sam0.c)
zephyr_library_sources_ifdef(CONFIG_I2C_LITEX i2c_litex.c)
zephyr_library_sources_ifdef(CONFIG_I2C_ASYNC i2c_async.c)

zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1
i2c_ll_stm32_v1.c
Expand Down
4 changes: 4 additions & 0 deletions drivers/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ config I2C_INIT_PRIORITY
help
I2C device driver initialization priority.

config I2C_ASYNC
bool "Enable I2C asynchronous API"
default n


module = I2C
module-str = i2c
Expand Down
4 changes: 4 additions & 0 deletions drivers/i2c/Kconfig.nrfx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ config I2C_0_NRF_TWI
config I2C_0_NRF_TWIM
def_bool $(dt_nodelabel_has_compat,i2c0,$(DT_COMPAT_NORDIC_NRF_TWIM))
select NRFX_TWIM0
select I2C_ASYNC
help
Enable nRF TWI Master with EasyDMA on port 0.
This peripheral accepts transfers from RAM only,
Expand All @@ -39,6 +40,7 @@ config I2C_1_NRF_TWI
config I2C_1_NRF_TWIM
def_bool $(dt_nodelabel_has_compat,i2c1,$(DT_COMPAT_NORDIC_NRF_TWIM))
select NRFX_TWIM1
select I2C_ASYNC
help
Enable nRF TWI Master with EasyDMA on port 1.
This peripheral accepts transfers from RAM only,
Expand All @@ -47,6 +49,7 @@ config I2C_1_NRF_TWIM
config I2C_2_NRF_TWIM
def_bool $(dt_nodelabel_has_compat,i2c2,$(DT_COMPAT_NORDIC_NRF_TWIM))
select NRFX_TWIM2
select I2C_ASYNC
help
Enable nRF TWI Master with EasyDMA on port 2.
This peripheral accepts transfers from RAM only,
Expand All @@ -55,6 +58,7 @@ config I2C_2_NRF_TWIM
config I2C_3_NRF_TWIM
def_bool $(dt_nodelabel_has_compat,i2c3,$(DT_COMPAT_NORDIC_NRF_TWIM))
select NRFX_TWIM3
select I2C_ASYNC
help
Enable nRF TWI Master with EasyDMA on port 3.
This peripheral accepts transfers from RAM only,
Expand Down
84 changes: 84 additions & 0 deletions drivers/i2c/i2c_async.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <drivers/i2c_async.h>
#include <sys/queued_seq.h>
#include <drivers/i2c.h>
#include <string.h>

static struct i2c_async *seq_mgr_to_i2c_async(struct sys_seq_mgr *mgr)
{
struct queued_seq_mgr *mgrs =
CONTAINER_OF(mgr, struct queued_seq_mgr, seq_mgr);
return CONTAINER_OF(mgrs, struct i2c_async, mgrs);
}

static void i2c_callback(struct device *dev,
struct sys_notify *notify,
int res)
{
struct i2c_async *i2c_async =
CONTAINER_OF(notify, struct i2c_async, action_notify);

sys_seq_finalize(&i2c_async->mgrs.seq_mgr, res, 0);
}

int i2c_async_sys_seq_xfer(struct sys_seq_mgr *mgr, void *data)
{
struct i2c_async *i2c_async = seq_mgr_to_i2c_async(mgr);
struct i2c_msg *msg = data;

sys_notify_init_callback(&i2c_async->action_notify, i2c_callback);
return i2c_single_transfer(i2c_async->dev,
msg,
i2c_async->addr,
&i2c_async->action_notify);
}

int i2c_async_sys_seq_address_set(struct sys_seq_mgr *mgr, void *data)
{
uint16_t *new_addr = data;
struct i2c_async *i2c_async = seq_mgr_to_i2c_async(mgr);

i2c_async->addr = *new_addr;

sys_seq_finalize(mgr, 0, 0);

return 0;
}

int i2c_async_init(struct i2c_async *i2c_async, struct device *dev)
{
i2c_async->dev = dev;
i2c_async->addr = I2C_INVALID_ADDRESS;

return queued_seq_init(&i2c_async->mgrs, NULL, &i2c_async->delay_timer);
}

int z_i2c_async_sync_transfer(struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr)
{
struct queued_seq qop;
int err;

for (uint8_t i = 0; i < num_msgs; i++) {
SYS_SEQ_DEFINE(, seq,
I2C_SEQ_ACTION_ADDRESS_SET(, addr),
I2C_SEQ_ACTION_MSG(, msgs[i].buf,
msgs[i].len, msgs[i].flags)
);
qop.seq = &seq;

err = queued_operation_sync_submit(
i2c_get_queued_operation_manager(dev),
&qop.qop,
QUEUED_OPERATION_PRIORITY_APPEND);
if (err < 0) {
return err;
}
}

return 0;
}
Loading