dma/cavs_hda: DMA driver for HDA on cAVS

Adds an initial driver for HDA streams on cAVS. A common code base is
provided for all HDA streams while the drivers are identified
differently as they have small behavior differences.

Uses dma_status to describe the positions for read/write. Uses dma_reload
to inform when to move the read/write positions. This closely follows
how HDA is being used in SoF

Simple test case is provided for both drivers.

Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
This commit is contained in:
Tom Burdick 2022-02-16 10:33:45 -06:00 committed by Anas Nashif
commit e018a3dff7
16 changed files with 582 additions and 2 deletions

View file

@ -17,3 +17,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_PL330 dma_pl330.c)
zephyr_library_sources_ifdef(CONFIG_DMA_IPROC_PAX dma_iproc_pax_v1.c) zephyr_library_sources_ifdef(CONFIG_DMA_IPROC_PAX dma_iproc_pax_v1.c)
zephyr_library_sources_ifdef(CONFIG_DMA_IPROC_PAX_V2 dma_iproc_pax_v2.c) zephyr_library_sources_ifdef(CONFIG_DMA_IPROC_PAX_V2 dma_iproc_pax_v2.c)
zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_GPDMA dma_cavs_gpdma.c dma_dw_common.c) zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_GPDMA dma_cavs_gpdma.c dma_dw_common.c)
zephyr_library_sources_ifdef(CONFIG_DMA_CAVS_HDA dma_cavs_hda.c dma_cavs_hda_host_in.c dma_cavs_hda_host_out.c)

View file

@ -46,4 +46,6 @@ source "drivers/dma/Kconfig.iproc_pax"
source "drivers/dma/Kconfig.cavs_gpdma" source "drivers/dma/Kconfig.cavs_gpdma"
source "drivers/dma/Kconfig.cavs_hda"
endif # DMA endif # DMA

View file

@ -0,0 +1,9 @@
# cAVS HDA configuration options
# Copyright (c) 2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
config DMA_CAVS_HDA
bool "Intel cAVS HDA DMA driver"
help
Intel cAVS HDA DMA driver.

145
drivers/dma/dma_cavs_hda.c Normal file
View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <drivers/dma.h>
#include <cavs_hda.h>
#include "dma_cavs_hda.h"
#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(dma_cavs_hda_dma);
/**
* @brief Intel CAVS HDA DMA (Stream) driver
*
* HDA is effectively, from the DSP, a ringbuffer (fifo) where the read
* and write positions are maintained by the hardware and the software may
* commit read/writes by writing to another register (DGFPBI) the length of
* the read or write.
*
* It's important that the software knows the position in the ringbuffer to read
* or write from. It's also important that the buffer be placed in the correct
* memory region and aligned to 128 bytes. Lastly it's important the host and
* dsp coordinate the order in which operations takes place. Doing all that
* HDA streams are a fantastic bit of hardware and do their job well.
*
* There are 4 types of streams, with a set of each available to be used to
* communicate to or from the Host or Link. Each stream set is uni directional.
*/
int cavs_hda_dma_host_in_config(const struct device *dev,
uint32_t channel,
struct dma_config *dma_cfg)
{
const struct cavs_hda_dma_cfg *const cfg = dev->config;
struct dma_block_config *blk_cfg;
uint8_t *buf;
__ASSERT(channel < cfg->dma_channels, "Channel does not exist");
__ASSERT(dma_cfg->block_count == 1,
"HDA does not support scatter gather or chained "
"block transfers.");
__ASSERT(dma_cfg->channel_direction == cfg->direction,
"Unexpected channel direction, HDA host in supports "
"MEMORY_TO_HOST");
blk_cfg = dma_cfg->head_block;
buf = (uint8_t *)(uintptr_t)(blk_cfg->source_address);
return cavs_hda_set_buffer(cfg->base, channel, buf,
blk_cfg->block_size);
}
int cavs_hda_dma_host_out_config(const struct device *dev,
uint32_t channel,
struct dma_config *dma_cfg)
{
const struct cavs_hda_dma_cfg *const cfg = dev->config;
uint8_t *buf;
struct dma_block_config *blk_cfg;
__ASSERT(channel < cfg->dma_channels, "Channel does not exist");
__ASSERT(dma_cfg->block_count == 1,
"HDA does not support scatter gather or chained "
"block transfers.");
__ASSERT(dma_cfg->channel_direction == cfg->direction,
"Unexpected channel direction, HDA host out supports "
"HOST_TO_MEMORY");
blk_cfg = dma_cfg->head_block;
buf = (uint8_t *)(uintptr_t)(blk_cfg->dest_address);
return cavs_hda_set_buffer(cfg->base, channel, buf,
blk_cfg->block_size);
}
int cavs_hda_dma_host_reload(const struct device *dev, uint32_t channel,
uint32_t src, uint32_t dst, size_t size)
{
const struct cavs_hda_dma_cfg *const cfg = dev->config;
__ASSERT(channel < cfg->dma_channels, "Channel does not exist");
cavs_hda_commit(cfg->base, channel, size);
return 0;
}
int cavs_hda_dma_status(const struct device *dev, uint32_t channel,
struct dma_status *stat)
{
const struct cavs_hda_dma_cfg *const cfg = dev->config;
__ASSERT(channel < cfg->dma_channels, "Channel does not exist");
stat->dir = cfg->direction;
stat->busy = *DGCS(cfg->base, channel) & DGCS_GBUSY;
stat->write_position = *DGBWP(cfg->base, channel);
stat->read_position = *DGBRP(cfg->base, channel);
return 0;
}
int cavs_hda_dma_start(const struct device *dev, uint32_t channel)
{
const struct cavs_hda_dma_cfg *const cfg = dev->config;
__ASSERT(channel < cfg->dma_channels, "Channel does not exist");
cavs_hda_enable(cfg->base, channel);
return 0;
}
int cavs_hda_dma_stop(const struct device *dev, uint32_t channel)
{
const struct cavs_hda_dma_cfg *const cfg = dev->config;
__ASSERT(channel < cfg->dma_channels, "Channel does not exist");
cavs_hda_disable(cfg->base, channel);
return 0;
}
int cavs_hda_dma_init(const struct device *dev)
{
struct cavs_hda_dma_data *data = dev->data;
const struct cavs_hda_dma_cfg *const cfg = dev->config;
for (uint32_t i = 0; i < cfg->dma_channels; i++) {
cavs_hda_init(cfg->base, i);
}
data->ctx.dma_channels = cfg->dma_channels;
data->ctx.atomic = data->channels_atomic;
data->ctx.magic = DMA_MAGIC;
LOG_INF("Intel cAVS HDA %s initialized", dev->name);
return 0;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_DMA_DMA_CAVS_HDA_COMMON_H_
#define ZEPHYR_DRIVERS_DMA_DMA_CAVS_HDA_COMMON_H_
#define CAVS_HDA_MAX_CHANNELS 32
#include <drivers/dma.h>
struct cavs_hda_dma_data {
struct dma_context ctx;
ATOMIC_DEFINE(channels_atomic, CAVS_HDA_MAX_CHANNELS);
};
struct cavs_hda_dma_cfg {
uint32_t base;
uint32_t dma_channels;
enum dma_channel_direction direction;
};
int cavs_hda_dma_host_in_config(const struct device *dev,
uint32_t channel,
struct dma_config *dma_cfg);
int cavs_hda_dma_host_out_config(const struct device *dev,
uint32_t channel,
struct dma_config *dma_cfg);
int cavs_hda_dma_host_reload(const struct device *dev, uint32_t channel,
uint32_t src, uint32_t dst, size_t size);
int cavs_hda_dma_status(const struct device *dev, uint32_t channel,
struct dma_status *stat);
int cavs_hda_dma_start(const struct device *dev, uint32_t channel);
int cavs_hda_dma_stop(const struct device *dev, uint32_t channel);
int cavs_hda_dma_init(const struct device *dev);
#endif /* ZEPHYR_DRIVERS_DMA_DMA_CAVS_HDA_COMMON_H_ */

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT intel_cavs_hda_host_in
#include <drivers/dma.h>
#include <cavs_hda.h>
#include "dma_cavs_hda.h"
#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(dma_cavs_hda_dma_host_in);
static const struct dma_driver_api cavs_hda_dma_host_in_api = {
.config = cavs_hda_dma_host_in_config,
.reload = cavs_hda_dma_host_reload,
.start = cavs_hda_dma_start,
.stop = cavs_hda_dma_stop,
.get_status = cavs_hda_dma_status,
};
#define CAVS_HDA_DMA_HOST_IN_INIT(inst) \
static const struct cavs_hda_dma_cfg cavs_hda_dma##inst##_config = { \
.base = DT_INST_REG_ADDR(inst), \
.dma_channels = DT_INST_PROP(inst, dma_channels), \
.direction = MEMORY_TO_HOST \
}; \
\
static struct cavs_hda_dma_data cavs_hda_dma##inst##_data = {}; \
\
DEVICE_DT_INST_DEFINE(inst, &cavs_hda_dma_init, NULL, &cavs_hda_dma##inst##_data, \
&cavs_hda_dma##inst##_config, POST_KERNEL, CONFIG_DMA_INIT_PRIORITY, \
&cavs_hda_dma_host_in_api);
DT_INST_FOREACH_STATUS_OKAY(CAVS_HDA_DMA_HOST_IN_INIT)

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT intel_cavs_hda_host_out
#include <drivers/dma.h>
#include <cavs_hda.h>
#include "dma_cavs_hda.h"
#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(dma_cavs_hda_dma_host_out);
static const struct dma_driver_api cavs_hda_dma_host_out_api = {
.config = cavs_hda_dma_host_out_config,
.reload = cavs_hda_dma_host_reload,
.start = cavs_hda_dma_start,
.stop = cavs_hda_dma_stop,
.get_status = cavs_hda_dma_status,
};
#define CAVS_HDA_DMA_HOST_OUT_INIT(inst) \
static const struct cavs_hda_dma_cfg cavs_hda_dma##inst##_config = { \
.base = DT_INST_REG_ADDR(inst), \
.dma_channels = DT_INST_PROP(inst, dma_channels), \
.direction = HOST_TO_MEMORY \
}; \
\
static struct cavs_hda_dma_data cavs_hda_dma##inst##_data = {}; \
\
DEVICE_DT_INST_DEFINE(inst, &cavs_hda_dma_init, NULL, &cavs_hda_dma##inst##_data, \
&cavs_hda_dma##inst##_config, POST_KERNEL, CONFIG_DMA_INIT_PRIORITY, \
&cavs_hda_dma_host_out_api);
DT_INST_FOREACH_STATUS_OKAY(CAVS_HDA_DMA_HOST_OUT_INIT)

View file

@ -0,0 +1,8 @@
# Copyright (c) 2022 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
description: Intel cAVS HDA Host In controller
compatible: "intel,cavs-hda-host-in"
include: intel,cavs-hda.yaml

View file

@ -0,0 +1,8 @@
# Copyright (c) 2022 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
description: Intel cAVS HDA Host Out controller
compatible: "intel,cavs-hda-host-out"
include: intel,cavs-hda.yaml

View file

@ -0,0 +1,16 @@
# Copyright (c) 2022 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Common fields for HDA DMA controllers
include: dma-controller.yaml
properties:
reg:
required: true
dma-channels:
required: true
"#dma-cells":
const: 1

View file

@ -31,5 +31,25 @@
status = "okay"; status = "okay";
}; };
hda_host_out: dma@0x72800 {
compatible = "intel,cavs-hda-host-out";
#dma-cells = <1>;
reg = <0x00072800 0x40>;
dma-channels = <7>;
label = "HDA_HOST_OUT";
status = "okay";
};
hda_host_in: dma@0x72c00 {
compatible = "intel,cavs-hda-host-in";
#dma-cells = <1>;
reg = <0x00072c00 0x40>;
dma-channels = <7>;
label = "HDA_HOST_IN";
status = "okay";
};
}; };
}; };

View file

@ -19,6 +19,10 @@ config DMA_CAVS_GPDMA
default y default y
depends on DMA depends on DMA
config DMA_CAVS_HDA
default y
depends on DMA
config I2S_CAVS config I2S_CAVS
default y default y
depends on I2S depends on I2S

View file

@ -4,4 +4,4 @@ cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(intel_adsp) project(intel_adsp)
target_sources(app PRIVATE src/main.c src/smoke.c) target_sources(app PRIVATE src/main.c src/smoke.c src/dma.c)

View file

@ -0,0 +1,242 @@
/* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include "arch/xtensa/cache.h"
#include <kernel.h>
#include <ztest.h>
#include <cavs_ipc.h>
#include <cavs_hda.h>
#include <drivers/dma.h>
#include "tests.h"
#define IPC_TIMEOUT K_MSEC(500)
#define DMA_BUF_SIZE 256
#define TRANSFER_SIZE 256
#define TRANSFER_COUNT 8
static __aligned(128) uint8_t dma_buf[DMA_BUF_SIZE];
static volatile int msg_cnt;
static volatile int msg_res;
static bool ipc_message(const struct device *dev, void *arg,
uint32_t data, uint32_t ext_data)
{
printk("HDA message received, data %u, ext_data %u\n", data, ext_data);
msg_res = data;
msg_cnt++;
return true;
}
/*
* Tests host input streams with the DMA API
*
* Note that the order of operations in this test are important and things potentially will not
* work in horrible and unexpected ways if not done as they are here.
*/
void test_hda_host_in_dma(void)
{
const struct device *dma;
int res, channel;
uint32_t last_msg_cnt;
printk("smoke testing hda with fifo buffer at address %p, size %d\n",
dma_buf, DMA_BUF_SIZE);
cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipc_message, NULL);
printk("Using buffer of size %d at addr %p\n", DMA_BUF_SIZE, dma_buf);
/* setup a ramp in the buffer */
for (uint32_t i = 0; i < DMA_BUF_SIZE; i++) {
dma_buf[i] = i & 0xff;
}
#if (IS_ENABLED(CONFIG_KERNEL_COHERENCE))
zassert_true(arch_mem_coherent(dma_buf), "Buffer is unexpectedly incoherent!");
#else
/* The buffer is in the cached address range and must be flushed */
zassert_false(arch_mem_coherent(dma_buf), "Buffer is unexpectedly coherent!");
z_xtensa_cache_flush(dma_buf, DMA_BUF_SIZE);
#endif
dma = device_get_binding("HDA_HOST_IN");
zassert_not_null(dma, "Expected a valid DMA device pointer");
channel = dma_request_channel(dma, NULL);
zassert_true(channel >= 0, "Expected a valid DMA channel");
printk("dma channel: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, channel, IPC_TIMEOUT);
printk("host reset: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_CONFIG,
channel | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
printk("host config: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
struct dma_block_config block_cfg = {
.block_size = DMA_BUF_SIZE,
.source_address = (uint32_t)(&dma_buf[0]),
};
struct dma_config dma_cfg = {
.block_count = 1,
.channel_direction = MEMORY_TO_HOST,
.head_block = &block_cfg,
};
res = dma_config(dma, channel, &dma_cfg);
printk("dsp dma config: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
zassert_ok(res, "Expected dma config to succeed");
res = dma_start(dma, channel);
printk("dsp dma start: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
zassert_ok(res, "Expected dma start to succeed");
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_START, channel, IPC_TIMEOUT);
printk("host start: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
for (uint32_t i = 0; i < TRANSFER_COUNT; i++) {
res = dma_reload(dma, channel, 0, 0, DMA_BUF_SIZE);
zassert_ok(res, "Expected dma reload to succeed");
printk("dsp dma reload: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
struct dma_status status;
int j;
/* up to 10mS wait time */
for (j = 0; j < 100; j++) {
res = dma_get_status(dma, channel, &status);
zassert_ok(res, "Expected dma status to succeed");
if (status.read_position == status.write_position) {
break;
}
k_busy_wait(100);
}
printk("dsp read write equal after %d uS: ", j*100);
cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, channel);
last_msg_cnt = msg_cnt;
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_VALIDATE, channel,
IPC_TIMEOUT);
WAIT_FOR(msg_cnt > last_msg_cnt);
zassert_true(msg_res == 1,
"Expected data validation to be true from Host");
}
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET,
channel, IPC_TIMEOUT);
res = dma_stop(dma, channel);
zassert_ok(res, "Expected dma stop to succeed");
}
/*
* Tests host output streams with the DMA API
*/
void test_hda_host_out_dma(void)
{
const struct device *dma;
int res, channel;
bool is_ramp;
printk("smoke testing hda with fifo buffer at address %p, size %d\n",
dma_buf, DMA_BUF_SIZE);
cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipc_message, NULL);
printk("Using buffer of size %d at addr %p\n", DMA_BUF_SIZE, dma_buf);
dma = device_get_binding("HDA_HOST_OUT");
zassert_not_null(dma, "Expected a valid DMA device pointer");
channel = dma_request_channel(dma, NULL);
zassert_true(channel >= 0, "Expected a valid DMA channel");
printk("dma channel: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET,
(channel + 7), IPC_TIMEOUT);
printk("host reset: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_CONFIG,
(channel + 7) | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
printk("host config: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
struct dma_block_config block_cfg = {
.block_size = DMA_BUF_SIZE,
.source_address = (uint32_t)(&dma_buf[0]),
};
struct dma_config dma_cfg = {
.block_count = 1,
.channel_direction = HOST_TO_MEMORY,
.head_block = &block_cfg,
};
res = dma_config(dma, channel, &dma_cfg);
printk("dsp dma config: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
zassert_ok(res, "Expected dma config to succeed");
res = dma_start(dma, channel);
printk("dsp dma start: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
zassert_ok(res, "Expected dma start to succeed");
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_START, (channel + 7), IPC_TIMEOUT);
printk("host start: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
for (uint32_t i = 0; i < TRANSFER_COUNT; i++) {
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_SEND,
(channel + 7) | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
printk("host send: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
/* TODO add a dma_poll() style call for xfer ready/complete maybe? */
WAIT_FOR(cavs_hda_buf_full(HDA_HOST_OUT_BASE, channel));
printk("dsp wait for full: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
#if (IS_ENABLED(CONFIG_KERNEL_COHERENCE))
zassert_true(arch_mem_coherent(dma_buf), "Buffer is unexpectedly incoherent!");
#else
/* The buffer is in the cached address range and must be invalidated
* prior to reading.
*/
zassert_false(arch_mem_coherent(dma_buf), "Buffer is unexpectedly coherent!");
z_xtensa_cache_inv(dma_buf, DMA_BUF_SIZE);
#endif
is_ramp = true;
for (int j = 0; j < DMA_BUF_SIZE; j++) {
printk("dma_buf[%d] = %d\n", j, dma_buf[j]);
if (dma_buf[j] != j) {
is_ramp = false;
}
}
zassert_true(is_ramp, "Expected data to be a ramp");
res = dma_reload(dma, channel, 0, 0, DMA_BUF_SIZE);
zassert_ok(res, "Expected dma reload to succeed");
printk("dsp dma reload: "); cavs_hda_dbg("host_out", HDA_HOST_IN_BASE, channel);
}
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, (channel + 7), IPC_TIMEOUT);
printk("host reset: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
res = dma_stop(dma, channel);
zassert_ok(res, "Expected dma stop to succeed");
printk("dsp dma stop: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, channel);
}

View file

@ -10,7 +10,8 @@ void test_main(void)
{ {
ztest_test_suite(intel_adsp_hda, ztest_test_suite(intel_adsp_hda,
ztest_unit_test(test_hda_host_in_smoke), ztest_unit_test(test_hda_host_in_smoke),
ztest_unit_test(test_hda_host_out_smoke) ztest_unit_test(test_hda_host_out_smoke),
ztest_unit_test(test_hda_host_in_dma)
); );
ztest_run_test_suite(intel_adsp_hda); ztest_run_test_suite(intel_adsp_hda);

View file

@ -13,6 +13,7 @@
void test_hda_host_in_smoke(void); void test_hda_host_in_smoke(void);
void test_hda_host_out_smoke(void); void test_hda_host_out_smoke(void);
void test_hda_host_in_dma(void);
static inline void hda_ipc_msg(const struct device *dev, uint32_t data, static inline void hda_ipc_msg(const struct device *dev, uint32_t data,
uint32_t ext, k_timeout_t timeout) uint32_t ext, k_timeout_t timeout)