Commit 5b7c462c authored by Emil Dahl Juhl's avatar Emil Dahl Juhl Committed by Benjamin Cabé
Browse files

rtio: executor: add relaxed RTIO_OP_AWAIT variant



Allow the rtio executor to handle the RTIO_OP_AWAIT for cases where no
rtio_iodev needs to be blocked while waiting for the signal.

When the RTIO_OP_AWAIT was introduced, the highlighted usecase was
performing transactions within an exact time window. This can be
achieved by blocking the rtio_iodev, effectively locking the bus, while
awaiting.

However, a relaxed implementation which doesn't block any rtio_iodev is
really useful for device drivers that use some kind of ready-event
during transactions. For example in order to read configuration out of
a sensor, the following flow may be required:

    1. Write some cmd like "read accelerometer scale"
    2. Await data-ready GPIO rising edge (might take "a long time")
    3. Read the requested data payload from the sensor

Using a relaxed variant of the RTIO_OP_AWAIT this can be elegantly put
together in a single chained rtio sequence. And by not blocking the
rtio_iodev, other devices can be operated during the await period.
Of course, not blocking the rtio_iodev also means that the bus may be
busy when the RTIO_OP_AWAIT completes and thus, step 3 may be blocked
for a some time and the guarantee of an exact time window cannot be
achieved with the relaxed variant.

To make the API clearer, separate helpers are added for the strict
(blocking iodev) and relaxed (blocking only the sqe chain) variant
respectively.

Signed-off-by: default avatarEmil Dahl Juhl <emil@s16s.ai>
parent 5574a81d
Loading
Loading
Loading
Loading
+48 −1
Original line number Diff line number Diff line
@@ -580,7 +580,7 @@ struct rtio_iodev {
/** An operation to sends I3C CCC */
#define RTIO_OP_I3C_CCC (RTIO_OP_I3C_CONFIGURE+1)

/** An operation to suspend bus while awaiting signal */
/** An operation to await a signal while blocking the iodev (if one is provided) */
#define RTIO_OP_AWAIT (RTIO_OP_I3C_CCC+1)

/**
@@ -745,6 +745,20 @@ static inline void rtio_sqe_prep_transceive(struct rtio_sqe *sqe,
	sqe->userdata = userdata;
}

/**
 * @brief Prepare an await op submission
 *
 * The await operation will await the completion signal before the sqe completes.
 *
 * If an rtio_iodev is provided then it will be blocked while awaiting. This facilitates a
 * low-latency continuation of the rtio sequence, a sort of "critical section" during a bus
 * operation if you will.
 * Note that it is the responsibility of the rtio_iodev driver to properly block during the
 * operation.
 *
 * See @ref rtio_sqe_prep_await_iodev for a helper, where an rtio_iodev is blocked.
 * See @ref rtio_sqe_prep_await_executor for a helper, where no rtio_iodev is blocked.
 */
static inline void rtio_sqe_prep_await(struct rtio_sqe *sqe,
				       const struct rtio_iodev *iodev,
				       int8_t prio,
@@ -757,6 +771,39 @@ static inline void rtio_sqe_prep_await(struct rtio_sqe *sqe,
	sqe->userdata = userdata;
}

/**
 * @brief Prepare an await op submission which blocks an rtio_iodev until completion
 *
 * This variant can be useful if the await op is part of a sequence which must run within a tight
 * time window as it effectively keeps the underlying bus locked while awaiting completion.
 * Note that it is the responsibility of the rtio_iodev driver to properly block during the
 * operation.
 *
 * See @ref rtio_sqe_prep_await for details.
 * See @ref rtio_sqe_prep_await_executor for a counterpart where no rtio_iodev is blocked.
 */
static inline void rtio_sqe_prep_await_iodev(struct rtio_sqe *sqe, const struct rtio_iodev *iodev,
					     int8_t prio, void *userdata)
{
	__ASSERT_NO_MSG(iodev != NULL);
	rtio_sqe_prep_await(sqe, iodev, prio, userdata);
}

/**
 * @brief Prepare an await op submission which completes the sqe after being signaled
 *
 * This variant can be useful when the await op serves as a logical piece of a sequence without
 * requirements for a low-latency continuation of the sequence upon completion, or if the await
 * op is expected to take "a long time" to complete.
 *
 * See @ref rtio_sqe_prep_await for details.
 * See @ref rtio_sqe_prep_await_iodev for a counterpart where an rtio_iodev is blocked.
 */
static inline void rtio_sqe_prep_await_executor(struct rtio_sqe *sqe, int8_t prio, void *userdata)
{
	rtio_sqe_prep_await(sqe, NULL, prio, userdata);
}

static inline void rtio_sqe_prep_delay(struct rtio_sqe *sqe,
				       k_timeout_t timeout,
				       void *userdata)
+19 −0
Original line number Diff line number Diff line
@@ -12,6 +12,22 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(rtio_executor, CONFIG_RTIO_LOG_LEVEL);

/**
 * @brief Callback which completes an RTIO_AWAIT_OP handled by the executor
 *
 * The callback is triggered when the rtio_sqe tied to the RTIO_AWAIT_OP
 * is signaled by the user.
 *
 * @param iodev_sqe Submission to complete
 * @param userdata Additional data passed along
 */
static void rtio_executor_sqe_signaled(struct rtio_iodev_sqe *iodev_sqe, void *userdata)
{
	ARG_UNUSED(userdata);

	rtio_iodev_sqe_ok(iodev_sqe, 0);
}

/**
 * @brief Executor handled submissions
 */
@@ -27,6 +43,9 @@ static void rtio_executor_op(struct rtio_iodev_sqe *iodev_sqe)
	case RTIO_OP_DELAY:
		rtio_sched_alarm(iodev_sqe, sqe->delay.timeout);
		break;
	case RTIO_OP_AWAIT:
		rtio_iodev_sqe_await_signal(iodev_sqe, rtio_executor_sqe_signaled, NULL);
		break;
	default:
		rtio_iodev_sqe_err(iodev_sqe, -EINVAL);
	}