From bfd46e1f541d56009d7ebbd325c3fe5ff19b356c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Tue, 21 Apr 2026 10:32:08 +0200 Subject: [PATCH 01/10] imx6ull-sdma: add missing const TASK: MSH-36 --- dma/imx6ull-sdma/libsdma.c | 4 ++-- dma/imx6ull-sdma/sdma-api.h | 1 + dma/imx6ull-sdma/sdma.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dma/imx6ull-sdma/libsdma.c b/dma/imx6ull-sdma/libsdma.c index f5f1b0eef..b5273ad1b 100644 --- a/dma/imx6ull-sdma/libsdma.c +++ b/dma/imx6ull-sdma/libsdma.c @@ -80,7 +80,7 @@ int sdma_channel_configure(sdma_t *s, sdma_channel_config_t *cfg) return sdma_dev_ctl(s, &dev_ctl, NULL, 0); } -int sdma_data_mem_write(sdma_t *s, void *data, size_t size, addr_t addr) +int sdma_data_mem_write(sdma_t *s, const void *data, size_t size, addr_t addr) { sdma_dev_ctl_t dev_ctl; @@ -88,7 +88,7 @@ int sdma_data_mem_write(sdma_t *s, void *data, size_t size, addr_t addr) dev_ctl.mem.addr = addr; dev_ctl.mem.len = size; - return sdma_dev_ctl(s, &dev_ctl, data, size); + return sdma_dev_ctl(s, &dev_ctl, (void *)data, size); } int sdma_data_mem_read(sdma_t *s, void *data, size_t size, addr_t addr) diff --git a/dma/imx6ull-sdma/sdma-api.h b/dma/imx6ull-sdma/sdma-api.h index 7baecdf3a..3a4bfbecf 100644 --- a/dma/imx6ull-sdma/sdma-api.h +++ b/dma/imx6ull-sdma/sdma-api.h @@ -124,6 +124,7 @@ typedef enum { sdma_dev_ctl__ocram_alloc } sdma_dev_ctl_type_t; +/* TODO: use separate types for input/output devctl and remove msg.o being used for input data (both raw and data) */ typedef struct { sdma_dev_ctl_type_t type; diff --git a/dma/imx6ull-sdma/sdma.h b/dma/imx6ull-sdma/sdma.h index 12cc3fbd5..fef5b7356 100644 --- a/dma/imx6ull-sdma/sdma.h +++ b/dma/imx6ull-sdma/sdma.h @@ -25,7 +25,7 @@ int sdma_close(sdma_t *s); int sdma_channel_configure(sdma_t *s, sdma_channel_config_t *cfg); -int sdma_data_mem_write(sdma_t *s, void *data, size_t size, addr_t addr); +int sdma_data_mem_write(sdma_t *s, const void *data, size_t size, addr_t addr); int sdma_data_mem_read(sdma_t *s, void *data, size_t size, addr_t addr); int sdma_context_dump(sdma_t *s, sdma_context_t *ctx); From a2fea65fea394e74e375fd762816f9025bf46607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Tue, 21 Apr 2026 10:32:49 +0200 Subject: [PATCH 02/10] imx6ull-sdma: format TASK: MSH-36 --- dma/imx6ull-sdma/imx6ull-sdma.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index 239a72b65..3b2f177cc 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -339,30 +339,22 @@ static int sdma_free_uncached(void *vaddr, size_t size) return munmap(vaddr, n*_PAGE_SIZE); } -static int __attribute__((unused)) sdma_program_memory_dump(uint16_t addr, - addr_t buffer, - size_t size) +static int __attribute__((unused)) sdma_program_memory_dump(uint16_t addr, addr_t buffer, size_t size) { return sdma_run_channel0_cmd(size, SDMA_CMD_C0_GET_PM, buffer, addr); } -static int __attribute__((unused)) sdma_program_memory_write(uint16_t addr, - addr_t buffer, - size_t size) +static int __attribute__((unused)) sdma_program_memory_write(uint16_t addr, addr_t buffer, size_t size) { return sdma_run_channel0_cmd(size, SDMA_CMD_C0_SET_PM, buffer, addr); } -static int sdma_data_memory_dump(uint16_t addr, - addr_t buffer, - size_t size) +static int sdma_data_memory_dump(uint16_t addr, addr_t buffer, size_t size) { return sdma_run_channel0_cmd(size, SDMA_CMD_C0_GET_DM, buffer, addr); } -static int sdma_data_memory_write(uint16_t addr, - addr_t buffer, - size_t size) +static int sdma_data_memory_write(uint16_t addr, addr_t buffer, size_t size) { return sdma_run_channel0_cmd(size, SDMA_CMD_C0_SET_DM, buffer, addr); } From 5178ca2cd786b6a1bc8738095d73d955e6872a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Wed, 13 May 2026 11:14:20 +0200 Subject: [PATCH 03/10] imx6ull-sdma: check size alignment in data read/write TASK: MSH-36 --- dma/imx6ull-sdma/imx6ull-sdma.c | 4 ++-- dma/imx6ull-sdma/sdma.h | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index 3b2f177cc..a4b5ec8af 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -698,7 +698,7 @@ static int dev_ctl(msg_t *msg) return EOK; case sdma_dev_ctl__data_mem_write: - if (msg->o.size != dev_ctl.mem.len || msg->o.size > common.tmp_size) { + if ((msg->o.size != dev_ctl.mem.len) || (msg->o.size > common.tmp_size) || (msg->o.size % 4 != 0)) { log_error("dev_ctl: invalid size"); return -EIO; } @@ -706,7 +706,7 @@ static int dev_ctl(msg_t *msg) return sdma_data_memory_write(dev_ctl.mem.addr, common.tmp_paddr, dev_ctl.mem.len); case sdma_dev_ctl__data_mem_read: - if (msg->o.size != dev_ctl.mem.len || msg->o.size > common.tmp_size) { + if ((msg->o.size != dev_ctl.mem.len) || (msg->o.size > common.tmp_size) || (msg->o.size % 4 != 0)) { log_error("dev_ctl: invalid size"); return -EIO; } diff --git a/dma/imx6ull-sdma/sdma.h b/dma/imx6ull-sdma/sdma.h index fef5b7356..7d23cb486 100644 --- a/dma/imx6ull-sdma/sdma.h +++ b/dma/imx6ull-sdma/sdma.h @@ -25,7 +25,21 @@ int sdma_close(sdma_t *s); int sdma_channel_configure(sdma_t *s, sdma_channel_config_t *cfg); +/* + * write SDMA memory using data mode (32 bit word access) + * + * @param[in] data,size source buffer, expressed in bytes, must be multiple of 4 bytes (32 bit word) + * @param[in] addr destination address in 32 bit words + */ int sdma_data_mem_write(sdma_t *s, const void *data, size_t size, addr_t addr); + + +/* + * read SDMA memory using data mode (32 bit word access) + * + * @param[out] data,size destination buffer; expressed in bytes; must be multiple of 4 bytes (32 bit word) + * @param[in] addr source address in 32 bit words + */ int sdma_data_mem_read(sdma_t *s, void *data, size_t size, addr_t addr); int sdma_context_dump(sdma_t *s, sdma_context_t *ctx); From 2cbb3fb3ba66ff6329b2eee36b2635b9ffd3fb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Tue, 21 Apr 2026 10:34:50 +0200 Subject: [PATCH 04/10] imx6ull-sdma: fix data memory read/write size calculation SDMA commands GET_DM and SET_DM take size in 32 bit words. TASK: MSH-36 --- dma/imx6ull-sdma/imx6ull-sdma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index a4b5ec8af..e540a2122 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -703,14 +703,14 @@ static int dev_ctl(msg_t *msg) return -EIO; } memcpy(common.tmp, msg->o.data, msg->o.size); - return sdma_data_memory_write(dev_ctl.mem.addr, common.tmp_paddr, dev_ctl.mem.len); + return sdma_data_memory_write(dev_ctl.mem.addr, common.tmp_paddr, dev_ctl.mem.len / 4); case sdma_dev_ctl__data_mem_read: if ((msg->o.size != dev_ctl.mem.len) || (msg->o.size > common.tmp_size) || (msg->o.size % 4 != 0)) { log_error("dev_ctl: invalid size"); return -EIO; } - res = sdma_data_memory_dump(dev_ctl.mem.addr, common.tmp_paddr, dev_ctl.mem.len); + res = sdma_data_memory_dump(dev_ctl.mem.addr, common.tmp_paddr, dev_ctl.mem.len / 4); memcpy(msg->o.data, common.tmp, msg->o.size); return res; From 378bcee0abe1eb5450e9da9e4091b06745bcb7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Wed, 29 Apr 2026 18:50:28 +0200 Subject: [PATCH 05/10] imx6ull-sdma: optimize lock usage Lock is always released inside condWait TASK: MSH-36 --- dma/imx6ull-sdma/imx6ull-sdma.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index e540a2122..c6b750b9e 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -1074,8 +1074,8 @@ int main(int argc, char *argv[]) unsigned i, intr_cnt[NUM_OF_SDMA_CHANNELS], cnt; memset(intr_cnt, 0, sizeof(intr_cnt)); + mutexLock(common.lock); while (1) { - mutexLock(common.lock); res = condWait(common.intr_cond, common.lock, INTR_WAIT_TIMEOUT_US); if (res == -ETIME) { @@ -1091,8 +1091,6 @@ int main(int argc, char *argv[]) common.broken = 1; } - - mutexUnlock(common.lock); continue; } @@ -1113,11 +1111,10 @@ int main(int argc, char *argv[]) condSignal(common.channel[i].intr_cond); intr_cnt[i] = cnt; } - - mutexUnlock(common.lock); } /* Should never be reached */ + mutexUnlock(common.lock); log_error("Exiting!"); return 0; } From 41578fed08fba415d2df525454b5918d752d6f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Tue, 12 May 2026 16:49:26 +0200 Subject: [PATCH 06/10] imx6ull-sdma: add README TASK: MSH-36 --- dma/imx6ull-sdma/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 dma/imx6ull-sdma/README.md diff --git a/dma/imx6ull-sdma/README.md b/dma/imx6ull-sdma/README.md new file mode 100644 index 000000000..ee5e8da67 --- /dev/null +++ b/dma/imx6ull-sdma/README.md @@ -0,0 +1,17 @@ +SDMA driver for IMX6ULL + +## (in)Frequently Asked Questions + +### Why there are references to BP / DSP / StarCore in IMX6 Reference Manual? + +It seems that SDMA documentation was copied from older devices using same IP core without proper care. Descriptions and +register were left with original terms that are not defined anywhere. + +* BP - Baseband Processor/Platform (aka. StarCore/DSP) +* AP - Application Processor/Platform + +There is limited amount of publicly available documentation but this [MXC300 fact-sheet] contains diagram that includes +SDMA with both application processor and DSP core. There are other diagrams here: [MOTOKRZR K3 – Theory of operation]. + +[MXC300 fact-sheet]: https://www.nxp.com/docs/en/fact-sheet/MXC300301FS.pdf +[MOTOKRZR K3 – Theory of operation]: https://firmware.center/firmware/Motorola/KRZR%20K3%20%28Sumba%29/Service%20Docs/MOTOKRZR_K3_Theory_of_Operation_v.1.0.pdf From 72fde8767f39ae7e349db71d84465df46acd4d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Mon, 4 May 2026 18:52:43 +0200 Subject: [PATCH 07/10] imx6ull-sdma: remove noop write to reserved register There is no need to `DSPOVR`o register. IMX6 does not have DSP core. It is set to 0xffffffff after reset and there is no need for any explicit writes from driver. Line `DSPOVR |= 1 << channel_id` looks like it might do something important. It does not. It makes `sdma_init_core` harder to understand without any benefit. TASK: MSH-36 --- dma/imx6ull-sdma/imx6ull-sdma.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index c6b750b9e..c79aabb8b 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -453,7 +453,6 @@ static void sdma_init_core(void) common.regs->EVTOVR = 0; common.regs->HOSTOVR = 0; - common.regs->DSPOVR = 0xffffffff; /* Clear channel pending status */ common.regs->EVTPEND = common.regs->EVTPEND; @@ -558,8 +557,6 @@ static int sdma_channel_configure(uint8_t channel_id, sdma_channel_config_t *cfg return -1; } - common.regs->DSPOVR |= 1 << channel_id; - if (cfg->trig == sdma_trig__event) { if (cfg->event >= NUM_OF_SDMA_REQUESTS) { log_error("event number is too high (%d)", cfg->event); From 541d76b6ea2dac1d2e3742a7e97c349eae6a21c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Fri, 26 Jun 2026 19:29:40 +0200 Subject: [PATCH 08/10] imx6ull-sdma: refactor sdma_intr TASK: MSH-36 --- dma/imx6ull-sdma/imx6ull-sdma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index c79aabb8b..d58a8f504 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -371,12 +371,13 @@ static int sdma_intr(unsigned int intr, void *arg) unsigned i; for (i = 1; i < NUM_OF_SDMA_CHANNELS; i++) { + sdma_channel_t *channel = &cmn->channel[i]; /* Check if channel is active and it's interrupt flag is set */ - if (_INTR & (1 << i) && cmn->channel[i].active) { + if (_INTR & (1 << i) && channel->active) { /* Set BD_DONE in all buffer descriptors */ - sdma_buffer_desc_t *current = cmn->channel[i].bd; + sdma_buffer_desc_t *current = channel->bd; do { if (!(current->flags & SDMA_BD_DONE)) current->flags |= SDMA_BD_DONE; @@ -384,7 +385,7 @@ static int sdma_intr(unsigned int intr, void *arg) /* Increase interrupt count to notify dispatcher that interrupt for * this channel occurred */ - cmn->channel[i].intr_cnt++; + channel->intr_cnt++; } } } From d12ab2a5a7a230d91979be92b63fed89581aed36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Fri, 17 Apr 2026 21:08:40 +0200 Subject: [PATCH 09/10] !imx6ull-sdma: make BD_DONE handing optional BD_DONE flags indicate that SDMA "owns" buffer described in BD and can write into it. When SDMA returns ownership to processor/driver buffer content should be read *before* setting BD_DONE flag. This ensures data integrity (no read / write collisions). This description assumes RX transfer, but same applies to TX with read / writes swapped. Automatic BD_DONE set can still be useful in some cases like sending fixed buffer repeatedaly (like read requests from ADC). Backwards incompatible. Use `channelCfg.options = sdma_chOption__auto_bd_done` to keep old behaviour. TASK: MSH-36 --- adc/ad7779/imx6ull/dma.c | 1 + dma/imx6ull-sdma/imx6ull-sdma.c | 19 +++++++++++-------- dma/imx6ull-sdma/sdma-api.h | 5 +++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/adc/ad7779/imx6ull/dma.c b/adc/ad7779/imx6ull/dma.c index 88aa1b1b3..475465cb1 100644 --- a/adc/ad7779/imx6ull/dma.c +++ b/adc/ad7779/imx6ull/dma.c @@ -69,6 +69,7 @@ static void sdma_configure(void) cfg.trig = sdma_trig__event; cfg.event = event_channel; cfg.priority = SDMA_CHANNEL_PRIORITY_MIN + 1; + cfg.options = sdma_chOption__auto_bd_done; sdma_channel_configure(&sdma_common.sdma, &cfg); } diff --git a/dma/imx6ull-sdma/imx6ull-sdma.c b/dma/imx6ull-sdma/imx6ull-sdma.c index d58a8f504..e546ade88 100644 --- a/dma/imx6ull-sdma/imx6ull-sdma.c +++ b/dma/imx6ull-sdma/imx6ull-sdma.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -135,7 +136,7 @@ typedef enum { typedef struct { int active; - int auto_bd_done; + bool auto_bd_done; sdma_buffer_desc_t *bd; addr_t bd_paddr; @@ -375,13 +376,14 @@ static int sdma_intr(unsigned int intr, void *arg) /* Check if channel is active and it's interrupt flag is set */ if (_INTR & (1 << i) && channel->active) { - - /* Set BD_DONE in all buffer descriptors */ - sdma_buffer_desc_t *current = channel->bd; - do { - if (!(current->flags & SDMA_BD_DONE)) - current->flags |= SDMA_BD_DONE; - } while (!((current++)->flags & SDMA_BD_WRAP)); + if (channel->auto_bd_done) { + /* Set BD_DONE in all buffer descriptors */ + sdma_buffer_desc_t *current = channel->bd; + do { + if (!(current->flags & SDMA_BD_DONE)) + current->flags |= SDMA_BD_DONE; + } while (!((current++)->flags & SDMA_BD_WRAP)); + } /* Increase interrupt count to notify dispatcher that interrupt for * this channel occurred */ @@ -574,6 +576,7 @@ static int sdma_channel_configure(uint8_t channel_id, sdma_channel_config_t *cfg common.regs->HOSTOVR &= ~(1 << channel_id); } + common.channel[channel_id].auto_bd_done = (cfg->options & sdma_chOption__auto_bd_done) != 0; sdma_set_channel_priority(channel_id, cfg->priority); if ((res = sdma_set_bd_array(channel_id, cfg->bd_paddr, cfg->bd_cnt)) < 0) { diff --git a/dma/imx6ull-sdma/sdma-api.h b/dma/imx6ull-sdma/sdma-api.h index 3a4bfbecf..cf365fee4 100644 --- a/dma/imx6ull-sdma/sdma-api.h +++ b/dma/imx6ull-sdma/sdma-api.h @@ -104,12 +104,17 @@ typedef enum { sdma_trig__host, } sdma_trig_t; +enum sdma_chOption { + sdma_chOption__auto_bd_done = (1 << 0), +}; + typedef struct { addr_t bd_paddr; /* Physical address of buffer descriptor array */ unsigned bd_cnt; sdma_trig_t trig; unsigned event; unsigned priority; + unsigned options; } sdma_channel_config_t; typedef enum { From 59c20739db01c9b4e4275245475aa49b5ea2d838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wi=C5=9Bniewski?= Date: Wed, 1 Apr 2026 13:51:26 +0200 Subject: [PATCH 10/10] adc/ade9113: add ADE9113 driver for IMX6ULL TASK: MSH-36 --- adc/ade9113/Makefile | 14 + adc/ade9113/ade9113.c | 171 +++++ adc/ade9113/ade9113.h | 36 + adc/ade9113/ade9113_regs.h | 570 +++++++++++++++ adc/ade9113/dma.c | 384 ++++++++++ adc/ade9113/dma.h | 35 + adc/ade9113/imx6ull.h | 81 +++ adc/ade9113/iomux_mux_enums.h | 1288 +++++++++++++++++++++++++++++++++ adc/ade9113/log.h | 29 + adc/ade9113/main.c | 635 ++++++++++++++++ adc/ade9113/msgapi.c | 151 ++++ adc/ade9113/msgapi.h | 34 + adc/ade9113/sample.c | 189 +++++ adc/ade9113/sample.h | 81 +++ 14 files changed, 3698 insertions(+) create mode 100644 adc/ade9113/Makefile create mode 100644 adc/ade9113/ade9113.c create mode 100644 adc/ade9113/ade9113.h create mode 100644 adc/ade9113/ade9113_regs.h create mode 100644 adc/ade9113/dma.c create mode 100644 adc/ade9113/dma.h create mode 100644 adc/ade9113/imx6ull.h create mode 100644 adc/ade9113/iomux_mux_enums.h create mode 100644 adc/ade9113/log.h create mode 100644 adc/ade9113/main.c create mode 100644 adc/ade9113/msgapi.c create mode 100644 adc/ade9113/msgapi.h create mode 100644 adc/ade9113/sample.c create mode 100644 adc/ade9113/sample.h diff --git a/adc/ade9113/Makefile b/adc/ade9113/Makefile new file mode 100644 index 000000000..ce699777a --- /dev/null +++ b/adc/ade9113/Makefile @@ -0,0 +1,14 @@ +# +# Driver for ADE9113 +# +# Copyright 2026 Phoenix Systems +# Author: Jan Wiśniewski +# +# SPDX-License-Identifier: BSD-3-Clause + +NAME := ade9113 +LOCAL_SRCS := main.c dma.c ade9113.c msgapi.c sample.c +DEP_LIBS := libsdma libimx6ull-ecspi +DEPS := imx6ull-gpio + +include $(binary.mk) diff --git a/adc/ade9113/ade9113.c b/adc/ade9113/ade9113.c new file mode 100644 index 000000000..829f4a39e --- /dev/null +++ b/adc/ade9113/ade9113.c @@ -0,0 +1,171 @@ +/* + * Phoenix-RTOS + * + * ADE9113 command/response handling (4 chained chips) + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include +#include + +#define LOG_TAG "ADE9113: " + +#include "log.h" +#include "ade9113_regs.h" +#include "ade9113.h" + + +static uint8_t crc8(const uint8_t *data, size_t len) +{ + const uint8_t poly = 0x07; + const uint8_t xorOut = 0x55; + uint8_t v = 0x00; + for (size_t i = 0; i < len; ++i) { + v = v ^ data[i]; + for (size_t b = 0; b < 8; ++b) { + v = (v >= 0x80) ? (v << 1) ^ poly : (v << 1); + } + } + return v ^ xorOut; +} + + +static uint16_t crc16(const uint8_t *data, size_t len) +{ + const uint16_t poly = 0x1021; + const uint16_t xorOut = 0x0000; + uint16_t v = 0xffff; + for (size_t i = 0; i < len; ++i) { + v = v ^ data[i] << 8; + for (size_t b = 0; b < 8; ++b) { + v = (v >= 0x8000) ? (v << 1) ^ poly : (v << 1); + } + } + return v ^ xorOut; +} + + +static inline int prepareRead(uint8_t reg, uint8_t *data, size_t size) +{ + if (size != 16) { + return -1; + } + memset(data, 0, 16); + data[12] = 0xC0; + data[13] = reg; + data[14] = 0x00; + data[15] = crc8(&data[12], 3); + return 16; +} + + +static int prepareWrite(uint8_t reg, uint8_t value, uint8_t *data, size_t size) +{ + if (size != 16) { + return -1; + } + memset(data, 0, 16); + data[12] = 0x40; + data[13] = reg; + data[14] = value; + data[15] = crc8(&data[12], 3); + return 16; +} + + +const char *ade9113_checkResponse(const uint8_t *data, size_t len) +{ + if (len != 16) { + return "invalid length"; + } + uint16_t crcA = crc16(data, 14); + uint16_t crcB = data[14] | (data[15] << 8); + if (crcA != crcB) { + return "CRC16 mismatch"; + } + if ((data[0] >> 7) != 0) { + /* read response */ + if ((data[0] & 0x02) != 0) { + return "read response CRC error bit set"; + } + } + else { + /* write response */ + if ((data[0] & 0x02) != 0) { + return "write response CRC error bit set"; + } + } + return NULL; +} + + +int ade9113_writeRegsDifferent(struct ade9113_ctx *ctx, uint8_t reg, uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + /* reverse order as first chip in chain receives last message */ + const uint8_t values[4] = { d, c, b, a }; + + uint8_t req[16 * 4] = { 0 }; + uint8_t rsp[16 * 4] = { 0 }; + + for (int i = 0; i < 4; ++i) { + prepareWrite(reg, values[i], &req[16 * i], 16); + } + ctx->spiExchange(ctx->userData, req, rsp, sizeof(req)); + + /* SCRATCH read is used here as NOP command to read result from actual write */ + for (int i = 0; i < 4; ++i) { + prepareRead(ADE9113_SCRATCH, &req[16 * i], 16); + } + ctx->spiExchange(ctx->userData, req, rsp, sizeof(req)); + + int ret = 0; + for (int i = 0; i < 4; ++i) { + const char *error = ade9113_checkResponse(&rsp[16 * i], 16); + if (error != NULL) { + log_error("write[%d] failed: %s", i, error); + ret = -1; + } + } + return ret; +} + + +int ade9113_readRegs(struct ade9113_ctx *ctx, uint8_t reg, uint8_t *values, uint8_t size) +{ + if (size != 4) { + return -1; + } + + uint8_t req[16 * 4] = { 0 }; + uint8_t rsp[16 * 4] = { 0 }; + + for (int i = 0; i < 4; ++i) { + prepareRead(reg, &req[16 * i], 16); + } + ctx->spiExchange(ctx->userData, req, rsp, sizeof(req)); + + /* SCRATCH read is used here as NOP command to read result from actual read */ + for (int i = 0; i < 4; ++i) { + prepareRead(ADE9113_SCRATCH, &req[16 * i], 16); + } + ctx->spiExchange(ctx->userData, req, rsp, sizeof(req)); + + int ret = 0; + for (int i = 0; i < 4; ++i) { + const char *error = ade9113_checkResponse(&rsp[16 * i], 16); + if (error != NULL) { + log_error("read[%d] failed: %s", i, error); + ret = -1; + } + } + for (int i = 0; i < 4; ++i) { + values[i] = rsp[(16 * (3 - i)) + 13]; + } + return ret; +} diff --git a/adc/ade9113/ade9113.h b/adc/ade9113/ade9113.h new file mode 100644 index 000000000..67feaaec0 --- /dev/null +++ b/adc/ade9113/ade9113.h @@ -0,0 +1,36 @@ +/* + * Phoenix-RTOS + * + * ADE9113 command/response handling (4 chained chips) + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include +#include + + +struct ade9113_ctx { + int (*spiExchange)(void *userData, const uint8_t *dataIn, uint8_t *dataOut, size_t len); + void *userData; +}; + + +const char *ade9113_checkResponse(const uint8_t *data, size_t len); + + +int ade9113_readRegs(struct ade9113_ctx *ctx, uint8_t reg, uint8_t *values, uint8_t size); + + +int ade9113_writeRegsDifferent(struct ade9113_ctx *ctx, uint8_t reg, uint8_t a, uint8_t b, uint8_t c, uint8_t d); + + +static inline int ade9113_writeRegs(struct ade9113_ctx *ctx, uint8_t reg, uint8_t a) +{ + return ade9113_writeRegsDifferent(ctx, reg, a, a, a, a); +} diff --git a/adc/ade9113/ade9113_regs.h b/adc/ade9113/ade9113_regs.h new file mode 100644 index 000000000..6a5e3129e --- /dev/null +++ b/adc/ade9113/ade9113_regs.h @@ -0,0 +1,570 @@ +/* + * Phoenix-RTOS + * + * ADE9113 register and field definitions + * + * NOTE: This file was generated from datasheet using internal tools. See + * (non-public) devtools repo for details. To be determined what is the + * correct way to upstream those tools and source data into public repo. + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/* clang-format off */ + +#ifndef ADE9113_REGS_H +#define ADE9113_REGS_H + +#include + +/* ------------------------------------------------------------------------ */ +/* Register addresses */ +/* ------------------------------------------------------------------------ */ + +#define ADE9113_SWRST 0x001 /* Software Reset. */ +#define ADE9113_CONFIG0 0x002 /* ADC Configuration. */ +#define ADE9113_CONFIG_FILT 0x003 /* Digital Filter Configuration. */ +#define ADE9113_CONFIG_ISO_ACC 0x005 /* Enable Access to Isolated (ISO) Register Space. The ISO side is Pin 1 to Pin 14. */ +#define ADE9113_CRC_RESULT_HI 0x006 /* Background Register Map CRC Most Significant Byte. */ +#define ADE9113_CRC_RESULT_LO 0x007 /* Background Register Map CRC Least Significant Byte. */ +#define ADE9113_EFUSE_REFRESH 0x008 /* eFuse Refresh. */ +#define ADE9113_EMI_CONFIG 0x009 /* Configure Isolation Frequency Hopping Method. */ +#define ADE9113_EMI_HI_MASK 0x00A /* Emissions Mask, High Frequency Bounds. */ +#define ADE9113_EMI_LO_MASK 0x00B /* Emissions Mask, Low Frequency Bounds. */ +#define ADE9113_EMI_HI_LIMIT 0x00C /* Factory Stored High Frequency Limit. Corresponds to emissions at 420 MHz. */ +#define ADE9113_EMI_MID_LIMIT 0x00D /* Factory Stored Center Frequency Value. Corresponds to emissions at 360 MHz. */ +#define ADE9113_EMI_LO_LIMIT 0x00E /* Factory Stored Low Frequency Limit. Corresponds to emissions at 300 MHz. */ +#define ADE9113_MASK0 0x00F /* Interrupt Mask 0 Register. Mask register for STATUS0. */ +#define ADE9113_MASK1 0x010 /* Interrupt Mask 1 Register. Mask register for STATUS1. */ +#define ADE9113_MASK2 0x011 /* Interrupt Mask 2 Register. Mask register for STATUS2. */ +#define ADE9113_CONFIG_ZX 0x012 /* Zero-Crossing Configuration. */ +#define ADE9113_SCRATCH 0x013 /* Software Debug Register for Testing SPI R/W. */ +#define ADE9113_SYNC_SNAP 0x014 /* ADC Synchronization Register. */ +#define ADE9113_SNAPSHOT_COUNT_HI 0x017 /* System Timing Controller Counter Most Significant Byte. */ +#define ADE9113_SNAPSHOT_COUNT_LO 0x018 /* System Timing Controller Counter Least Significant Byte. */ +#define ADE9113_WR_LOCK 0x01F /* Configuration Lock Register. */ +#define ADE9113_STATUS0 0x020 /* Latched Status of High Priority Interrupts. */ +#define ADE9113_STATUS1 0x021 /* Latched Status of Low Priority Interrupts. */ +#define ADE9113_STATUS2 0x022 /* Latched Status of Isolated ADC Interrupts. */ +#define ADE9113_COM_FLT_TYPE 0x023 /* ISO to NONISO Communications Fault Type. */ +#define ADE9113_COM_FLT_COUNT 0x024 /* ISO to NONISO Communications Fault Count. */ +#define ADE9113_CONFIG_CRC 0x025 /* Configuration of Background Register Map CRC. */ +#define ADE9113_I_WAV_HI 0x026 /* Current Channel Waveform Data Most Significant Byte. */ +#define ADE9113_I_WAV_MD 0x027 /* Current Channel Waveform Data Middle Byte. */ +#define ADE9113_I_WAV_LO 0x028 /* Current Channel Waveform Data Least Significant Byte. */ +#define ADE9113_V1_WAV_HI 0x029 /* V1 Channel Waveform Data Most Significant Byte. */ +#define ADE9113_V1_WAV_MD 0x02A /* V1 Channel Waveform Data Middle Byte. */ +#define ADE9113_V1_WAV_LO 0x02B /* V1 Channel Waveform Data Least Significant Byte. */ +#define ADE9113_V2_WAV_HI 0x02C /* V2 Channel Waveform Data Most Significant Byte. */ +#define ADE9113_V2_WAV_MD 0x02D /* V2 Channel Waveform Data Middle Byte. */ +#define ADE9113_V2_WAV_LO 0x02E /* V2 Channel Waveform Data Least Significant Byte. */ +#define ADE9113_UNIQUE_PART_ID_5 0x075 /* Unique Part ID Most Significant Byte (Byte 5). */ +#define ADE9113_UNIQUE_PART_ID_4 0x076 /* Unique Part ID Byte 4. */ +#define ADE9113_UNIQUE_PART_ID_3 0x077 /* Unique Part ID Byte 3. */ +#define ADE9113_UNIQUE_PART_ID_2 0x078 /* Unique Part ID Byte 2. */ +#define ADE9113_UNIQUE_PART_ID_1 0x079 /* Unique Part ID Byte 1. */ +#define ADE9113_UNIQUE_PART_ID_0 0x07A /* Unique Part ID Least Significant Byte (Byte 0). */ +#define ADE9113_SILICON_REVISION 0x07D /* Revision Value of ISO and NONISO Silicon. */ +#define ADE9113_VERSION_PRODUCT 0x07E /* Product Version Identifier. */ +#define ADE9113_DC_OFFSET_MODE 0x0CC /* Enable the Current Channel Input Short. */ + +/* ------------------------------------------------------------------------ */ +/* Field enumerations */ +/* ------------------------------------------------------------------------ */ + +enum ade9113_swrst { + ADE9113_SWRST__SOFTWARE_RESET_COMMAND = 0xD6, /* Software Reset Command. The software resets all registers to their default values. Power cycling the device may be required to correct hardware functionality. */ +}; + +enum ade9113_config0_stream_dbg { + ADE9113_CONFIG0_STREAM_DBG__NORMAL_MODE = 0x00, /* Normal Mode. The x_WAV_x registers contain the ADC results. */ + ADE9113_CONFIG0_STREAM_DBG__STATIC_MODE = 0x01, /* Static Mode. The x_WAV_x registers are static and hold their value. The x_WAV_x registers can be written to change to a new value. The x_WAV_x registers become static and hold their value until a register write is performed to the x_WAV_x register with a new value. Programming must first be enabled with the count mode. setting before this static mode functions. */ + ADE9113_CONFIG0_STREAM_DBG__COUNT_MODE = 0x02, /* Count Mode. Data Increments at ADC Conversion Rate. Enables write access to x_WAV_x registers and increments the x_WAV_x register with every DREADY pulse. */ + ADE9113_CONFIG0_STREAM_DBG__RESERVED = 0x03, /* Reserved. Same operation as normal mode. */ +}; + +enum ade9113_config_filt_lpf_bw { + ADE9113_CONFIG_FILT_LPF_BW__BANDWIDTH_2K7 = 0x00, /* Bandwidth of 2.7 kHz at 8 kSPS output data rate. */ + ADE9113_CONFIG_FILT_LPF_BW__BANDWIDTH_3K3 = 0x01, /* Bandwidth of 3.3 kHz at 8 kSPS output data rate. */ +}; + +enum ade9113_config_filt_datapath_config { + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__32_KHZ = 0x00, /* Sinc3, 32 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__LPF_32_KHZ = 0x01, /* Sinc3, LPF Enabled, 32 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__COMP_LPF_32_KHZ = 0x02, /* Sinc3, Compensation Enabled, LPF Enabled, 32 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__LPF_8_KHZ = 0x03, /* Sinc3, LPF Enabled, 8 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__COMP_LPF_8_KHZ = 0x04, /* Sinc3, Compensation Enabled, LPF Enabled, 8 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__LPF_4_KHZ = 0x05, /* Sinc3, LPF Enabled, 4 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__LPF_2_KHZ = 0x06, /* Sinc3, LPF Enabled, 2 kHz Sampling. */ + ADE9113_CONFIG_FILT_DATAPATH_CONFIG__LPF_1_KHZ = 0x07, /* Sinc3, LPF Enabled, 1 kHz Sampling. */ +}; + +enum ade9113_emi_config_emi_config { + ADE9113_EMI_CONFIG_EMI_CONFIG__SAWTOOTH_FREQUENCY_RISING = 0x00, /* Sawtooth Frequency Rising. Frequency starts at frequency defined by EMI_LO_LIMIT and ramps to higher frequency as defined by EMI_HI_LIMIT and then returns to EMI_LO_LIMIT. */ + ADE9113_EMI_CONFIG_EMI_CONFIG__SAWTOOTH_FREQUENCY_FALLING = 0x01, /* Sawtooth Frequency Falling. Frequency starts at frequency defined by EMI_HI_LIMIT and ramps to a lower frequency as defined by EMI_LO_LIMIT and then returns to EMI_LO_LIMIT. */ + ADE9113_EMI_CONFIG_EMI_CONFIG__RAMP = 0x02, /* Ramp. Linear ramp up and down in frequency between the limits, EMI_LO_LIMIT and EMI_HI_LIMIT. */ + ADE9113_EMI_CONFIG_EMI_CONFIG__RANDOM_HOPPING_FREQUENCY = 0x03, /* Random Hopping Frequency. The isolated power oscillator frequency varies +/-63 trim codes around the calibrated center frequency of EMI_MID_LIMIT. */ +}; + +enum ade9113_config_zx_zx_edge_sel { + ADE9113_CONFIG_ZX_ZX_EDGE_SEL__PASS_SIGN = 0x00, /* ZX Pin Reflects the Sign of the Input Signal. The ZX pin goes high on negative to positive ZX and low on positive to negative ZX. */ + ADE9113_CONFIG_ZX_ZX_EDGE_SEL__PULSE_RAISING = 0x01, /* Detect Zero Crossings with Positive Slope. When zero crossing from negative to positive, a high pulse duration of 512 µs is generated. */ + ADE9113_CONFIG_ZX_ZX_EDGE_SEL__PULSE_FALLING = 0x02, /* Detect Zero Crossings with Negative Slope. When zero crossing from positive to negative, a high pulse duration of 512 µs is generated. */ + ADE9113_CONFIG_ZX_ZX_EDGE_SEL__PULSE_ZERO_CROSS = 0x03, /* Detect Zero Crossings with Positive or Negative Slopes. Combines 01 and 10. */ +}; + +enum ade9113_config_zx_zx_channel_config { + ADE9113_CONFIG_ZX_ZX_CHANNEL_CONFIG__ZX_OUT_DISABLE = 0x00, /* Disable Zero-Crossing Output. */ + ADE9113_CONFIG_ZX_ZX_CHANNEL_CONFIG__ZX_OUT_I = 0x01, /* Output Zero-Crossing Function from the Current Channel on the ZX Pin. */ + ADE9113_CONFIG_ZX_ZX_CHANNEL_CONFIG__ZX_OUT_V1 = 0x02, /* Output Zero-Crossing Function from the V1 Channel on the ZX Pin. */ + ADE9113_CONFIG_ZX_ZX_CHANNEL_CONFIG__ZX_OUT_V2 = 0x03, /* Output Zero-Crossing Function from the V2 Channel on the ZX Pin. */ +}; + +enum ade9113_wr_lock { + ADE9113_WR_LOCK__LOCK_KEY = 0xD4, /* Lock Key. If register map is locked, the lock key value can be read. */ + ADE9113_WR_LOCK__UNLOCK_KEY = 0x5E, /* Unlock Key. if the register map is unlocked, any value can be written or read from this location. */ +}; + +enum ade9113_version_product { + ADE9113_VERSION_PRODUCT__ADE9113 = 0x00, /* ADE9113. */ + ADE9113_VERSION_PRODUCT__ADE9112 = 0x01, /* ADE9112. */ + ADE9113_VERSION_PRODUCT__ADE9103 = 0x03, /* ADE9103. */ +}; + +/* ------------------------------------------------------------------------ */ +/* Register structs + encode / decode */ +/* ------------------------------------------------------------------------ */ + +/* ADC Configuration. */ +struct ade9113_config0 { + uint8_t clkout_en; + uint8_t crc_en_spi_write; + enum ade9113_config0_stream_dbg stream_dbg; +}; + +static inline uint8_t ade9113_config0_encode(struct ade9113_config0 v) +{ + return ((v.clkout_en & 0x01) << 0) | ((v.crc_en_spi_write & 0x01) << 1) | ((v.stream_dbg & 0x03) << 2); +} + +#define ADE9113_CONFIG0_ENC(...) \ + ade9113_config0_encode((struct ade9113_config0){__VA_ARGS__}) + +static inline struct ade9113_config0 ade9113_config0_decode(uint8_t r) +{ + return (struct ade9113_config0){ + .clkout_en = (r >> 0) & 0x01, + .crc_en_spi_write = (r >> 1) & 0x01, + .stream_dbg = (r >> 2) & 0x03, + }; +} + +/* Digital Filter Configuration. */ +struct ade9113_config_filt { + enum ade9113_config_filt_datapath_config datapath_config; + enum ade9113_config_filt_lpf_bw lpf_bw; + uint8_t i_adc_invert; + uint8_t v1_adc_invert; + uint8_t v2_adc_invert; +}; + +static inline uint8_t ade9113_config_filt_encode(struct ade9113_config_filt v) +{ + return ((v.datapath_config & 0x07) << 0) | ((v.lpf_bw & 0x01) << 3) | ((v.i_adc_invert & 0x01) << 4) | ((v.v1_adc_invert & 0x01) << 5) | ((v.v2_adc_invert & 0x01) << 6); +} + +#define ADE9113_CONFIG_FILT_ENC(...) \ + ade9113_config_filt_encode((struct ade9113_config_filt){__VA_ARGS__}) + +static inline struct ade9113_config_filt ade9113_config_filt_decode(uint8_t r) +{ + return (struct ade9113_config_filt){ + .datapath_config = (r >> 0) & 0x07, + .lpf_bw = (r >> 3) & 0x01, + .i_adc_invert = (r >> 4) & 0x01, + .v1_adc_invert = (r >> 5) & 0x01, + .v2_adc_invert = (r >> 6) & 0x01, + }; +} + +/* Enable Access to Isolated (ISO) Register Space. The ISO side is Pin 1 to Pin 14. */ +struct ade9113_config_iso_acc { + uint8_t iso_wr_acc_en; +}; + +static inline uint8_t ade9113_config_iso_acc_encode(struct ade9113_config_iso_acc v) +{ + return ((v.iso_wr_acc_en & 0x01) << 0); +} + +#define ADE9113_CONFIG_ISO_ACC_ENC(...) \ + ade9113_config_iso_acc_encode((struct ade9113_config_iso_acc){__VA_ARGS__}) + +static inline struct ade9113_config_iso_acc ade9113_config_iso_acc_decode(uint8_t r) +{ + return (struct ade9113_config_iso_acc){ + .iso_wr_acc_en = (r >> 0) & 0x01, + }; +} + +/* eFuse Refresh. */ +struct ade9113_efuse_refresh { + uint8_t efuse_refresh; +}; + +static inline uint8_t ade9113_efuse_refresh_encode(struct ade9113_efuse_refresh v) +{ + return ((v.efuse_refresh & 0x01) << 0); +} + +#define ADE9113_EFUSE_REFRESH_ENC(...) \ + ade9113_efuse_refresh_encode((struct ade9113_efuse_refresh){__VA_ARGS__}) + +static inline struct ade9113_efuse_refresh ade9113_efuse_refresh_decode(uint8_t r) +{ + return (struct ade9113_efuse_refresh){ + .efuse_refresh = (r >> 0) & 0x01, + }; +} + +/* Configure Isolation Frequency Hopping Method. */ +struct ade9113_emi_config { + enum ade9113_emi_config_emi_config emi_config; +}; + +static inline uint8_t ade9113_emi_config_encode(struct ade9113_emi_config v) +{ + return ((v.emi_config & 0x07) << 0); +} + +#define ADE9113_EMI_CONFIG_ENC(...) \ + ade9113_emi_config_encode((struct ade9113_emi_config){__VA_ARGS__}) + +static inline struct ade9113_emi_config ade9113_emi_config_decode(uint8_t r) +{ + return (struct ade9113_emi_config){ + .emi_config = (r >> 0) & 0x07, + }; +} + +/* Interrupt Mask 0 Register. Mask register for STATUS0. */ +struct ade9113_mask0 { + uint8_t comflt_err; + uint8_t spi_crc_err; + uint8_t crc_chg; + uint8_t com_up; + uint8_t status2x; + uint8_t status1x; +}; + +static inline uint8_t ade9113_mask0_encode(struct ade9113_mask0 v) +{ + return ((v.comflt_err & 0x01) << 0) | ((v.spi_crc_err & 0x01) << 1) | ((v.crc_chg & 0x01) << 3) | ((v.com_up & 0x01) << 4) | ((v.status2x & 0x01) << 6) | ((v.status1x & 0x01) << 7); +} + +#define ADE9113_MASK0_ENC(...) \ + ade9113_mask0_encode((struct ade9113_mask0){__VA_ARGS__}) + +static inline struct ade9113_mask0 ade9113_mask0_decode(uint8_t r) +{ + return (struct ade9113_mask0){ + .comflt_err = (r >> 0) & 0x01, + .spi_crc_err = (r >> 1) & 0x01, + .crc_chg = (r >> 3) & 0x01, + .com_up = (r >> 4) & 0x01, + .status2x = (r >> 6) & 0x01, + .status1x = (r >> 7) & 0x01, + }; +} + +/* Interrupt Mask 1 Register. Mask register for STATUS1. */ +struct ade9113_mask1 { + uint8_t adc_sync_done; + uint8_t i_wav_ovrng; + uint8_t v1_wav_ovrng; + uint8_t v2_wav_ovrng; +}; + +static inline uint8_t ade9113_mask1_encode(struct ade9113_mask1 v) +{ + return ((v.adc_sync_done & 0x01) << 0) | ((v.i_wav_ovrng & 0x01) << 1) | ((v.v1_wav_ovrng & 0x01) << 2) | ((v.v2_wav_ovrng & 0x01) << 3); +} + +#define ADE9113_MASK1_ENC(...) \ + ade9113_mask1_encode((struct ade9113_mask1){__VA_ARGS__}) + +static inline struct ade9113_mask1 ade9113_mask1_decode(uint8_t r) +{ + return (struct ade9113_mask1){ + .adc_sync_done = (r >> 0) & 0x01, + .i_wav_ovrng = (r >> 1) & 0x01, + .v1_wav_ovrng = (r >> 2) & 0x01, + .v2_wav_ovrng = (r >> 3) & 0x01, + }; +} + +/* Interrupt Mask 2 Register. Mask register for STATUS2. */ +struct ade9113_mask2 { + uint8_t iso_test_mmr_err; + uint8_t iso_dig_mod_i_ovf; + uint8_t iso_dig_mod_v1_ovf; + uint8_t iso_dig_mod_v2_ovf; + uint8_t iso_efuse_mem_err; + uint8_t iso_phy_crc_err; + uint8_t iso_clk_stbl_err; +}; + +static inline uint8_t ade9113_mask2_encode(struct ade9113_mask2 v) +{ + return ((v.iso_test_mmr_err & 0x01) << 0) | ((v.iso_dig_mod_i_ovf & 0x01) << 1) | ((v.iso_dig_mod_v1_ovf & 0x01) << 2) | ((v.iso_dig_mod_v2_ovf & 0x01) << 3) | ((v.iso_efuse_mem_err & 0x01) << 4) | ((v.iso_phy_crc_err & 0x01) << 5) | ((v.iso_clk_stbl_err & 0x01) << 6); +} + +#define ADE9113_MASK2_ENC(...) \ + ade9113_mask2_encode((struct ade9113_mask2){__VA_ARGS__}) + +static inline struct ade9113_mask2 ade9113_mask2_decode(uint8_t r) +{ + return (struct ade9113_mask2){ + .iso_test_mmr_err = (r >> 0) & 0x01, + .iso_dig_mod_i_ovf = (r >> 1) & 0x01, + .iso_dig_mod_v1_ovf = (r >> 2) & 0x01, + .iso_dig_mod_v2_ovf = (r >> 3) & 0x01, + .iso_efuse_mem_err = (r >> 4) & 0x01, + .iso_phy_crc_err = (r >> 5) & 0x01, + .iso_clk_stbl_err = (r >> 6) & 0x01, + }; +} + +/* Zero-Crossing Configuration. */ +struct ade9113_config_zx { + enum ade9113_config_zx_zx_channel_config zx_channel_config; + enum ade9113_config_zx_zx_edge_sel zx_edge_sel; +}; + +static inline uint8_t ade9113_config_zx_encode(struct ade9113_config_zx v) +{ + return ((v.zx_channel_config & 0x03) << 0) | ((v.zx_edge_sel & 0x03) << 2); +} + +#define ADE9113_CONFIG_ZX_ENC(...) \ + ade9113_config_zx_encode((struct ade9113_config_zx){__VA_ARGS__}) + +static inline struct ade9113_config_zx ade9113_config_zx_decode(uint8_t r) +{ + return (struct ade9113_config_zx){ + .zx_channel_config = (r >> 0) & 0x03, + .zx_edge_sel = (r >> 2) & 0x03, + }; +} + +/* ADC Synchronization Register. */ +struct ade9113_sync_snap { + uint8_t snapshot; + uint8_t align; + uint8_t prep_broadcast; +}; + +static inline uint8_t ade9113_sync_snap_encode(struct ade9113_sync_snap v) +{ + return ((v.snapshot & 0x01) << 0) | ((v.align & 0x01) << 1) | ((v.prep_broadcast & 0x01) << 2); +} + +#define ADE9113_SYNC_SNAP_ENC(...) \ + ade9113_sync_snap_encode((struct ade9113_sync_snap){__VA_ARGS__}) + +static inline struct ade9113_sync_snap ade9113_sync_snap_decode(uint8_t r) +{ + return (struct ade9113_sync_snap){ + .snapshot = (r >> 0) & 0x01, + .align = (r >> 1) & 0x01, + .prep_broadcast = (r >> 2) & 0x01, + }; +} + +/* System Timing Controller Counter Most Significant Byte. */ +struct ade9113_snapshot_count_hi { + uint8_t snapshot_count; +}; + +static inline uint8_t ade9113_snapshot_count_hi_encode(struct ade9113_snapshot_count_hi v) +{ + return ((v.snapshot_count & 0x3F) << 0); +} + +#define ADE9113_SNAPSHOT_COUNT_HI_ENC(...) \ + ade9113_snapshot_count_hi_encode((struct ade9113_snapshot_count_hi){__VA_ARGS__}) + +static inline struct ade9113_snapshot_count_hi ade9113_snapshot_count_hi_decode(uint8_t r) +{ + return (struct ade9113_snapshot_count_hi){ + .snapshot_count = (r >> 0) & 0x3F, + }; +} + +/* Latched Status of High Priority Interrupts. */ +struct ade9113_status0 { + uint8_t comflt_err; + uint8_t spi_crc_err; + uint8_t efuse_mem_err; + uint8_t crc_chg; + uint8_t com_up; + uint8_t reset_done; + uint8_t status2x; + uint8_t status1x; +}; + +static inline uint8_t ade9113_status0_encode(struct ade9113_status0 v) +{ + return ((v.comflt_err & 0x01) << 0) | ((v.spi_crc_err & 0x01) << 1) | ((v.efuse_mem_err & 0x01) << 2) | ((v.crc_chg & 0x01) << 3) | ((v.com_up & 0x01) << 4) | ((v.reset_done & 0x01) << 5) | ((v.status2x & 0x01) << 6) | ((v.status1x & 0x01) << 7); +} + +#define ADE9113_STATUS0_ENC(...) \ + ade9113_status0_encode((struct ade9113_status0){__VA_ARGS__}) + +static inline struct ade9113_status0 ade9113_status0_decode(uint8_t r) +{ + return (struct ade9113_status0){ + .comflt_err = (r >> 0) & 0x01, + .spi_crc_err = (r >> 1) & 0x01, + .efuse_mem_err = (r >> 2) & 0x01, + .crc_chg = (r >> 3) & 0x01, + .com_up = (r >> 4) & 0x01, + .reset_done = (r >> 5) & 0x01, + .status2x = (r >> 6) & 0x01, + .status1x = (r >> 7) & 0x01, + }; +} + +/* Latched Status of Low Priority Interrupts. */ +struct ade9113_status1 { + uint8_t adc_sync_done; + uint8_t i_wav_ovrng; + uint8_t v1_wav_ovrng; + uint8_t v2_wav_ovrng; +}; + +static inline uint8_t ade9113_status1_encode(struct ade9113_status1 v) +{ + return ((v.adc_sync_done & 0x01) << 0) | ((v.i_wav_ovrng & 0x01) << 1) | ((v.v1_wav_ovrng & 0x01) << 2) | ((v.v2_wav_ovrng & 0x01) << 3); +} + +#define ADE9113_STATUS1_ENC(...) \ + ade9113_status1_encode((struct ade9113_status1){__VA_ARGS__}) + +static inline struct ade9113_status1 ade9113_status1_decode(uint8_t r) +{ + return (struct ade9113_status1){ + .adc_sync_done = (r >> 0) & 0x01, + .i_wav_ovrng = (r >> 1) & 0x01, + .v1_wav_ovrng = (r >> 2) & 0x01, + .v2_wav_ovrng = (r >> 3) & 0x01, + }; +} + +/* Latched Status of Isolated ADC Interrupts. */ +struct ade9113_status2 { + uint8_t iso_test_mmr_err; + uint8_t iso_dig_mod_i_ovf; + uint8_t iso_dig_mod_v1_ovf; + uint8_t iso_dig_mod_v2_ovf; + uint8_t iso_efuse_mem_err; + uint8_t iso_phy_crc_err; + uint8_t iso_clk_stbl_err; +}; + +static inline uint8_t ade9113_status2_encode(struct ade9113_status2 v) +{ + return ((v.iso_test_mmr_err & 0x01) << 0) | ((v.iso_dig_mod_i_ovf & 0x01) << 1) | ((v.iso_dig_mod_v1_ovf & 0x01) << 2) | ((v.iso_dig_mod_v2_ovf & 0x01) << 3) | ((v.iso_efuse_mem_err & 0x01) << 4) | ((v.iso_phy_crc_err & 0x01) << 5) | ((v.iso_clk_stbl_err & 0x01) << 6); +} + +#define ADE9113_STATUS2_ENC(...) \ + ade9113_status2_encode((struct ade9113_status2){__VA_ARGS__}) + +static inline struct ade9113_status2 ade9113_status2_decode(uint8_t r) +{ + return (struct ade9113_status2){ + .iso_test_mmr_err = (r >> 0) & 0x01, + .iso_dig_mod_i_ovf = (r >> 1) & 0x01, + .iso_dig_mod_v1_ovf = (r >> 2) & 0x01, + .iso_dig_mod_v2_ovf = (r >> 3) & 0x01, + .iso_efuse_mem_err = (r >> 4) & 0x01, + .iso_phy_crc_err = (r >> 5) & 0x01, + .iso_clk_stbl_err = (r >> 6) & 0x01, + }; +} + +/* ISO to NONISO Communications Fault Type. */ +struct ade9113_com_flt_type { + uint8_t iso_ecc_err; + uint8_t iso_phy_err; + uint8_t iso_status_rd_ecc_e_rr; +}; + +static inline uint8_t ade9113_com_flt_type_encode(struct ade9113_com_flt_type v) +{ + return ((v.iso_ecc_err & 0x01) << 0) | ((v.iso_phy_err & 0x01) << 1) | ((v.iso_status_rd_ecc_e_rr & 0x01) << 2); +} + +#define ADE9113_COM_FLT_TYPE_ENC(...) \ + ade9113_com_flt_type_encode((struct ade9113_com_flt_type){__VA_ARGS__}) + +static inline struct ade9113_com_flt_type ade9113_com_flt_type_decode(uint8_t r) +{ + return (struct ade9113_com_flt_type){ + .iso_ecc_err = (r >> 0) & 0x01, + .iso_phy_err = (r >> 1) & 0x01, + .iso_status_rd_ecc_e_rr = (r >> 2) & 0x01, + }; +} + +/* Configuration of Background Register Map CRC. */ +struct ade9113_config_crc { + uint8_t crc_force; + uint8_t crc_done; +}; + +static inline uint8_t ade9113_config_crc_encode(struct ade9113_config_crc v) +{ + return ((v.crc_force & 0x01) << 0) | ((v.crc_done & 0x01) << 1); +} + +#define ADE9113_CONFIG_CRC_ENC(...) \ + ade9113_config_crc_encode((struct ade9113_config_crc){__VA_ARGS__}) + +static inline struct ade9113_config_crc ade9113_config_crc_decode(uint8_t r) +{ + return (struct ade9113_config_crc){ + .crc_force = (r >> 0) & 0x01, + .crc_done = (r >> 1) & 0x01, + }; +} + +/* Revision Value of ISO and NONISO Silicon. */ +struct ade9113_silicon_revision { + uint8_t iso_chip_rev; + uint8_t noniso_chip_rev; +}; + +static inline uint8_t ade9113_silicon_revision_encode(struct ade9113_silicon_revision v) +{ + return ((v.iso_chip_rev & 0x0F) << 0) | ((v.noniso_chip_rev & 0x0F) << 4); +} + +#define ADE9113_SILICON_REVISION_ENC(...) \ + ade9113_silicon_revision_encode((struct ade9113_silicon_revision){__VA_ARGS__}) + +static inline struct ade9113_silicon_revision ade9113_silicon_revision_decode(uint8_t r) +{ + return (struct ade9113_silicon_revision){ + .iso_chip_rev = (r >> 0) & 0x0F, + .noniso_chip_rev = (r >> 4) & 0x0F, + }; +} + +#endif /* ADE9113_REGS_H */ + +/* clang-format: on */ diff --git a/adc/ade9113/dma.c b/adc/ade9113/dma.c new file mode 100644 index 000000000..2ac62ba7b --- /dev/null +++ b/adc/ade9113/dma.c @@ -0,0 +1,384 @@ +/* + * Phoenix-RTOS + * + * ADE9113 driver DMA for IMX6ULL + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "ADE9113(DMA): " + +#include "log.h" + +#define SDMA_FILE_CH_TX "/dev/sdma/ch07" +#define SDMA_FILE_CH_RX "/dev/sdma/ch08" + +#define IMX6ULL_SDMA_EVENT_ECSPI1_RX (3) +#define IMX6ULL_SDMA_EVENT_ECSPI1_TX (4) +#define IMX6ULL_ECSPI1_BASE (0x02008000U) + +#define SDMA_ECSPI_BASE IMX6ULL_ECSPI1_BASE +#define SDMA_EVENT_RX IMX6ULL_SDMA_EVENT_ECSPI1_RX +#define SDMA_EVENT_TX IMX6ULL_SDMA_EVENT_ECSPI1_TX + +#define IRQ_BASE (32) +#define IRQ_SDMA (IRQ_BASE + 2) + +#define ADE9113_CHAIN_LEN 4 +#define ADE9113_REQ_SIZE 16 +#define SDMA_TX_DESCRIPTORS 2 + +struct sdmaBuffer { + size_t size; + size_t count; + addr_t phys_addr; + void *ptr; +}; + + +struct sdmaChannel { + sdma_t sdma; + struct sdmaBuffer buffer; + struct sdmaBuffer bd; +}; + + +struct sdmaCtx { + volatile sig_atomic_t enabled; + struct sdmaChannel tx; + struct sdmaChannel rx; + handle_t rxCond; + handle_t rxLock; + handle_t rxThreadId; + bool (*rxCb)(const uint8_t *data, size_t size); +}; + + +struct sdmaCtx sdma_common; + + +static int sdmaBufferAlloc(sdma_t *sdma, struct sdmaBuffer *buffer, size_t size, size_t count) +{ + *buffer = (struct sdmaBuffer) { + .size = size, + .count = count, + .phys_addr = 0, + .ptr = NULL + }; + buffer->ptr = sdma_alloc_uncached(sdma, size * count, &buffer->phys_addr, 1); + return (buffer->ptr != NULL) ? 0 : -1; +} + + +static void sdmaBufferFree(sdma_t *sdma, struct sdmaBuffer *buffer) +{ + if (buffer->ptr == NULL) { + return; + } + sdma_free_uncached(buffer->ptr, buffer->count * buffer->size); + *buffer = (struct sdmaBuffer) { + .size = 0, + .count = 0, + .phys_addr = 0, + .ptr = NULL + }; +} + + +static void resetDescriptor(struct sdmaChannel *ch, size_t idx) +{ + if (idx >= ch->bd.count) { + return; + } + bool isLast = idx + 1 == ch->bd.count; + volatile sdma_buffer_desc_t *bd = (sdma_buffer_desc_t *)ch->bd.ptr; + /* TODO: check this line can be skipped when handingIrq */ + bd[idx] = (struct sdma_buffer_desc_s) { + .count = ch->buffer.size, + .flags = SDMA_BD_CONT | SDMA_BD_INTR | (isLast ? SDMA_BD_WRAP : 0), + .command = SDMA_CMD_MODE_32_BIT, + /* buf->count is a special case for tx that reuses single buffer */ + .buffer_addr = ch->buffer.phys_addr + (ch->buffer.count > 1 ? ch->buffer.size * idx : 0) + }; + bd[idx].flags |= SDMA_BD_DONE; +} + + +static int handleIrq(unsigned int irqNum, void *voidCtx) +{ + struct sdmaCtx *ctx = (struct sdmaCtx *)voidCtx; + bool wakeupThread = false; + if (ctx->enabled != 0) { + struct sdmaChannel *ch = &ctx->tx; + volatile sdma_buffer_desc_t *bd = ch->bd.ptr; + for (size_t i = 0; i < ch->bd.count; ++i) { + if ((bd[i].flags & SDMA_BD_DONE) != 0) { + continue; /* descriptor still owned by SDMA */ + } + /* TX data is fixed so we can immediately give BD owenership back to SDMA (set BD_DONE flag) */ + resetDescriptor(ch, i); + } + } + + if (ctx->enabled != 0) { + struct sdmaChannel *ch = &ctx->rx; + volatile sdma_buffer_desc_t *bd = ch->bd.ptr; + for (size_t i = 0; i < ch->bd.count; ++i) { + if ((bd[i].flags & SDMA_BD_DONE) != 0) { + continue; /* descriptor still owned by SDMA */ + } + /* thread will consume data and set BD_DONE flag */ + wakeupThread = true; + break; + } + } + return wakeupThread ? 0 : -1; +} + + +static void handleThread(void *voidCtx) +{ + struct sdmaCtx *ctx = (struct sdmaCtx *)voidCtx; + mutexLock(ctx->rxLock); + while (ctx->enabled != 0) { + /* TODO: add order check (should alternate between both descriptors) */ + struct sdmaChannel *ch = &ctx->rx; + volatile sdma_buffer_desc_t *bd = ch->bd.ptr; + for (size_t i = 0; i < ch->bd.count; ++i) { + if ((bd[i].flags & SDMA_BD_DONE) != 0) { + continue; /* descriptor still owned by SDMA */ + } + bool keepGoing = true; + if (ctx->rxCb != NULL) { + const uint8_t *data = (const uint8_t *)ch->buffer.ptr + (ch->buffer.size * i); + keepGoing = ctx->rxCb(data, ch->buffer.size); + } + if (keepGoing) { + resetDescriptor(ch, i); + } + } + condWait(ctx->rxCond, ctx->rxLock, 0); + } + mutexUnlock(ctx->rxLock); + endthread(); +} + + +struct channelConfig { + sdma_script_t script; + uint8_t event; + unsigned priority; + addr_t fifoAddr; + unsigned fifoWatermark; +}; + + +static void configureChannel(struct sdmaChannel *ch, const struct channelConfig *cfg) +{ + for (int i = 0; i < ch->bd.count; ++i) { + resetDescriptor(ch, i); + } + sdma_context_t sdma_context; + sdma_context_init(&sdma_context); + sdma_context_set_pc(&sdma_context, cfg->script); + sdma_context.gr[0] = (cfg->event < 32) ? 0 : 1 << (cfg->event - 32); /* Event2_mask */ + sdma_context.gr[1] = (cfg->event < 32) ? (1 << cfg->event) : 0; + sdma_context.gr[6] = cfg->fifoAddr; + sdma_context.gr[7] = cfg->fifoWatermark; + sdma_context_set(&ch->sdma, &sdma_context); + + sdma_channel_configure( + &ch->sdma, + &(sdma_channel_config_t) { + .bd_paddr = ch->bd.phys_addr, + .bd_cnt = ch->bd.count, + .trig = sdma_trig__event, + .event = cfg->event, + .priority = cfg->priority, + .options = 0 }); +} + + +static void sdmaConfigure(struct sdmaCtx *ctx) +{ + struct sdmaChannel *ch = &ctx->tx; + { + /* + * Fill request buffer with identical read requests. Long responses always include sample data so we can read + * any register. Using SCRATCH register to allow to differentiate responses from each device. + * + * Order of bytes is reversed in 4 byte words. DMA/SPI will send this request in following order: + * req[3], req[2], req[1], req[0], req[7], req[6], ... + */ + const uint8_t adcRequest[ADE9113_REQ_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x13, 0xc0 }; + for (size_t offs = 0; offs + ADE9113_REQ_SIZE <= ch->buffer.size; offs += ADE9113_REQ_SIZE) { + memcpy((uint8_t *)ch->buffer.ptr + offs, adcRequest, ADE9113_REQ_SIZE); + } + } + /* + * NOTE: "AP" scripts are used here as there were issues observed with "SHP" scripts. + * + * When shp_2_mcu was used and main processor was interacting with other SPBA peripherals (ie. UART) random RX + * words were lost. It looked like some read instructions (SDMA `ld` instruction) on RX FIFO were executed twice + * resulting in one FIFO element being silently lost. Similar TX issues were observed with mcu_2_shp. + * + * It is not clear at this point if this is caused by configuration error (ie. clock setup) or if this is inherent + * platform / hw issue. "AP" scripts work good enough for this use-case. + * + * This post describes similar issue: + * https://community.nxp.com/t5/i-MX-Processors/SDMA-data-corrupt-under-CPU-load/td-p/1571766 + */ + configureChannel( + &ctx->rx, + &(const struct channelConfig) { + .event = SDMA_EVENT_RX, + .script = sdma_script__ap_2_mcu, + .fifoAddr = SDMA_ECSPI_BASE + 0, /* eCSPI->RXDATA */ + .fifoWatermark = ADE9113_REQ_SIZE * ADE9113_CHAIN_LEN, /* The WML is always given in bytes */ + .priority = SDMA_CHANNEL_PRIORITY_MIN + 1 }); + configureChannel( + &ctx->tx, + &(const struct channelConfig) { + .event = SDMA_EVENT_TX, + .script = sdma_script__mcu_2_ap, + .fifoAddr = SDMA_ECSPI_BASE + 4, /* eCSPI->TXDATA */ + .fifoWatermark = ADE9113_REQ_SIZE * ADE9113_CHAIN_LEN, /* The WML is always given in bytes */ + .priority = SDMA_CHANNEL_PRIORITY_MIN + 1 }); +} + + +static int sdmaInit(struct sdmaCtx *const ctx, size_t size, size_t count) +{ + if ((size % _PAGE_SIZE) != 0) { + log_error("buffer size is not aligned to %d", _PAGE_SIZE); + return -1; + } + + interrupt(IRQ_SDMA, handleIrq, ctx, ctx->rxCond, 0); + + const char *files[] = { SDMA_FILE_CH_RX, SDMA_FILE_CH_TX }; + struct sdmaChannel *channels[] = { &ctx->rx, &ctx->tx }; + for (int i = 0; i < 2; ++i) { + unsigned tries = 25; + while (sdma_open(&channels[i]->sdma, files[i]) < 0) { + usleep(100 * 1000); + if (--tries == 0) { + log_error("failed to open SDMA device file (%s)", files[i]); + return -1; + } + } + } + + /* + * NOTE: OCRAM usage could be optimized here by using single allocation for descriptors of both channels. + * It is would be little bit hacky in current API as allocations are on per channel bases. + */ + + if (sdmaBufferAlloc(&ctx->tx.sdma, &ctx->tx.buffer, _PAGE_SIZE * 4, 1) < 0) { + return -1; + } + if (sdmaBufferAlloc(&ctx->tx.sdma, &ctx->tx.bd, sizeof(sdma_buffer_desc_t), 2) < 0) { + return -1; + } + + if (sdmaBufferAlloc(&ctx->rx.sdma, &ctx->rx.buffer, size, count) < 0) { + return -1; + } + if (sdmaBufferAlloc(&ctx->rx.sdma, &ctx->rx.bd, sizeof(sdma_buffer_desc_t), count) < 0) { + return -1; + } + + sdmaConfigure(ctx); + return 0; +} + + +static int sdmaReset(struct sdmaCtx *ctx) +{ + sdmaConfigure(ctx); + return 0; +} + + +static void sdmaFree(struct sdmaCtx *const ctx) +{ + sdmaBufferFree(&ctx->tx.sdma, &ctx->tx.buffer); + sdmaBufferFree(&ctx->tx.sdma, &ctx->tx.bd); + sdmaBufferFree(&ctx->rx.sdma, &ctx->rx.buffer); + sdmaBufferFree(&ctx->rx.sdma, &ctx->rx.bd); +} + + +void dma_enable(void) +{ + struct sdmaCtx *const ctx = &sdma_common; + + ctx->enabled = 1; + static uint8_t handleStack[_PAGE_SIZE] __attribute__((aligned(8))); + beginthreadex(handleThread, 0, handleStack, sizeof(handleStack), ctx, &ctx->rxThreadId); + + sdma_enable(&ctx->rx.sdma); + sdma_enable(&ctx->tx.sdma); +} + + +void dma_disable(void) +{ + struct sdmaCtx *const ctx = &sdma_common; + + mutexLock(ctx->rxLock); + ctx->enabled = 0; + condBroadcast(ctx->rxCond); + mutexUnlock(ctx->rxLock); + + threadJoin(ctx->rxThreadId, 0); + + usleep(50 * 1000); + + sdma_disable(&ctx->tx.sdma); + sdma_disable(&ctx->rx.sdma); +} + + +int dma_init(size_t size, size_t count, bool (*rxCb)(const uint8_t *data, size_t size)) +{ + struct sdmaCtx *const ctx = &sdma_common; + + condCreate(&ctx->rxCond); + mutexCreate(&ctx->rxLock); + ctx->rxCb = rxCb; + + if (sdmaInit(ctx, size, count) < 0) { + sdmaFree(ctx); + log_error("dma_init failed\n"); + return -EIO; + } + return EOK; +} + + +int dma_reset(void) +{ + if (sdmaReset(&sdma_common) < 0) { + return -EIO; + } + return EOK; +} diff --git a/adc/ade9113/dma.h b/adc/ade9113/dma.h new file mode 100644 index 000000000..001f98b52 --- /dev/null +++ b/adc/ade9113/dma.h @@ -0,0 +1,35 @@ +/* + * Phoenix-RTOS + * + * ADE9113 driver DMA for IMX6ULL + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ADE9113_DMA_H +#define ADE9113_DMA_H + +#include +#include +#include + + +int dma_init(size_t size, size_t count, bool (*rxCb)(const uint8_t *data, size_t size)); + + +int dma_reset(void); + + +void dma_free(void); + + +void dma_enable(void); + + +void dma_disable(void); + + +#endif diff --git a/adc/ade9113/imx6ull.h b/adc/ade9113/imx6ull.h new file mode 100644 index 000000000..6d21e5194 --- /dev/null +++ b/adc/ade9113/imx6ull.h @@ -0,0 +1,81 @@ +/* + * Phoenix-RTOS + * + * IMX6ULL periphery registers for PWM, GPIO and ECSPI + * + * NOTE: This file was generated from datasheet using internal tools. See + * (non-public) devtools repo for details. To be determined what is the + * correct way to upstream those tools and source data into public repo. + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/* clang-format off */ + + +#include + +typedef const uint32_t reg32_ro_t; +typedef uint32_t reg32_rw_t; +typedef uint32_t reg32_wo_t; +typedef uint32_t reg32_w1c_t; + +/* PWM */ +struct imx6ull_regs_pwm { + reg32_rw_t PWMCR; /* 0x0000 - Control */ + reg32_w1c_t PWMSR; /* 0x0004 - Status */ + reg32_rw_t PWMIR; /* 0x0008 - Interrupt */ + reg32_rw_t PWMSAR; /* 0x000C - Sample */ + reg32_rw_t PWMPR; /* 0x0010 - Period */ + reg32_ro_t PWMCNR; /* 0x0014 - Counter */ +}; +_Static_assert(offsetof(struct imx6ull_regs_pwm, PWMCNR) == 0x0014, "wrong offset"); + +#define IMX_PWM3_BASE 0x02088000U + + +/* GPIO */ +struct imx6ull_regs_gpio { + reg32_rw_t DR; /* 0x0000 - data register */ + reg32_rw_t GDIR; /* 0x0004 - direction register */ + reg32_ro_t PSR; /* 0x0008 - pad status register */ + reg32_rw_t ICR1; /* 0x000C - interrupt configuration register1 */ + reg32_rw_t ICR2; /* 0x0010 - interrupt configuration register2 */ + reg32_rw_t IMR; /* 0x0014 - interrupt mask register */ + reg32_w1c_t ISR; /* 0x0018 - interrupt status register */ + reg32_rw_t EDGE_SEL; /* 0x001C - edge select register */ +}; +_Static_assert(offsetof(struct imx6ull_regs_gpio, EDGE_SEL) == 0x001C, "wrong offset"); + + +#define IMX_GPIO1_BASE 0x0209C000U +#define IMX_GPIO2_BASE 0x020A0000U +#define IMX_GPIO3_BASE 0x020A4000U +#define IMX_GPIO4_BASE 0x020A8000U +#define IMX_GPIO5_BASE 0x020AC000U + + +/* ECSPI */ +struct imx6ull_regs_ecspi { + reg32_ro_t RXDATA; /* 0x0000 - Receive Data */ + reg32_wo_t TXDATA; /* 0x0004 - Transmit Data */ + reg32_rw_t CONREG; /* 0x0008 - Control */ + reg32_rw_t CONFIGREG; /* 0x000C - Config */ + reg32_rw_t INTREG; /* 0x0010 - Interrupt Control */ + reg32_rw_t DMAREG; /* 0x0014 - DMA Control */ + reg32_rw_t STATREG; /* 0x0018 - Status */ + reg32_rw_t PERIODREG; /* 0x001C - Sample Period Control */ + reg32_rw_t TESTREG; /* 0x0020 - Test Control */ + reg32_ro_t _reserved0[7]; /* 0x0024 */ + reg32_wo_t MSGDATA; /* 0x0040 - Message Data */ +}; +_Static_assert(offsetof(struct imx6ull_regs_ecspi, MSGDATA) == 0x0040, "wrong offset"); + +#define IMX_ECSPI1_BASE 0x02008000U +#define IMX_ECSPI1 ((volatile struct imx6ull_regs_ecspi *)IMX_ECSPI1_BASE) + +/* clang-format: on */ diff --git a/adc/ade9113/iomux_mux_enums.h b/adc/ade9113/iomux_mux_enums.h new file mode 100644 index 000000000..e9547e781 --- /dev/null +++ b/adc/ade9113/iomux_mux_enums.h @@ -0,0 +1,1288 @@ +/* + * Phoenix-RTOS + * + * IMX6ULL mux definitions + * + * NOTE: This file was generated from datasheet using internal tools. See + * (non-public) devtools repo for details. To be determined what is the + * correct way to upstream those tools and source data into public repo. + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/* clang-format off */ +#ifndef IOMUX_MUX_ENUMS_H +#define IOMUX_MUX_ENUMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + ALT0 = 0, + ALT1 = 1, + ALT2 = 2, + ALT3 = 3, + ALT4 = 4, + ALT5 = 5, + ALT6 = 6, + ALT7 = 7, + ALT8 = 8, + ALT9 = 9 +}; + +enum MUX_PAD_BOOT_MODE0 { + MUX_PAD_BOOT_MODE0__GPIO5_IO10 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER0 { + MUX_PAD_SNVS_TAMPER0__GPIO5_IO00 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER1 { + MUX_PAD_SNVS_TAMPER1__GPIO5_IO01 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER2 { + MUX_PAD_SNVS_TAMPER2__GPIO5_IO02 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER3 { + MUX_PAD_SNVS_TAMPER3__GPIO5_IO03 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER4 { + MUX_PAD_SNVS_TAMPER4__GPIO5_IO04 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER5 { + MUX_PAD_SNVS_TAMPER5__GPIO5_IO05 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER6 { + MUX_PAD_SNVS_TAMPER6__GPIO5_IO06 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER7 { + MUX_PAD_SNVS_TAMPER7__GPIO5_IO07 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER8 { + MUX_PAD_SNVS_TAMPER8__GPIO05_IO08 = ALT5 +}; + +enum MUX_PAD_SNVS_TAMPER9 { + MUX_PAD_SNVS_TAMPER9__GPIO5_IO09 = ALT5 +}; + +enum MUX_PAD_CSI_DATA00 { + MUX_PAD_CSI_DATA00__CSI_DATA02 = ALT0, + MUX_PAD_CSI_DATA00__USDHC2_DATA0 = ALT1, + MUX_PAD_CSI_DATA00__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA00__ECSPI2_SCLK = ALT3, + MUX_PAD_CSI_DATA00__EIM_AD00 = ALT4, + MUX_PAD_CSI_DATA00__GPIO4_IO21 = ALT5, + MUX_PAD_CSI_DATA00__SRC_INT_BOOT = ALT6, + MUX_PAD_CSI_DATA00__UART5_TX = ALT8, + MUX_PAD_CSI_DATA00__ESAI_TX_HF_CLK = ALT9 +}; + +enum MUX_PAD_CSI_DATA01 { + MUX_PAD_CSI_DATA01__CSI_DATA03 = ALT0, + MUX_PAD_CSI_DATA01__USDHC2_DATA1 = ALT1, + MUX_PAD_CSI_DATA01__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA01__ECSPI2_SS0 = ALT3, + MUX_PAD_CSI_DATA01__EIM_AD01 = ALT4, + MUX_PAD_CSI_DATA01__GPIO4_IO22 = ALT5, + MUX_PAD_CSI_DATA01__SAI1_MCLK = ALT6, + MUX_PAD_CSI_DATA01__UART5_RX = ALT8, + MUX_PAD_CSI_DATA01__ESAI_RX_HF_CLK = ALT9 +}; + +enum MUX_PAD_CSI_DATA02 { + MUX_PAD_CSI_DATA02__CSI_DATA04 = ALT0, + MUX_PAD_CSI_DATA02__USDHC2_DATA2 = ALT1, + MUX_PAD_CSI_DATA02__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA02__ECSPI2_MOSI = ALT3, + MUX_PAD_CSI_DATA02__EIM_AD02 = ALT4, + MUX_PAD_CSI_DATA02__GPIO4_IO23 = ALT5, + MUX_PAD_CSI_DATA02__SAI1_RX_SYNC = ALT6, + MUX_PAD_CSI_DATA02__UART5_RTS_B = ALT8, + MUX_PAD_CSI_DATA02__ESAI_RX_FS = ALT9 +}; + +enum MUX_PAD_CSI_DATA03 { + MUX_PAD_CSI_DATA03__CSI_DATA05 = ALT0, + MUX_PAD_CSI_DATA03__USDHC2_DATA3 = ALT1, + MUX_PAD_CSI_DATA03__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA03__ECSPI2_MISO = ALT3, + MUX_PAD_CSI_DATA03__EIM_AD03 = ALT4, + MUX_PAD_CSI_DATA03__GPIO4_IO24 = ALT5, + MUX_PAD_CSI_DATA03__SAI1_RX_BCLK = ALT6, + MUX_PAD_CSI_DATA03__UART5_CTS_B = ALT8, + MUX_PAD_CSI_DATA03__ESAI_RX_CLK = ALT9 +}; + +enum MUX_PAD_CSI_DATA04 { + MUX_PAD_CSI_DATA04__CSI_DATA06 = ALT0, + MUX_PAD_CSI_DATA04__USDHC2_DATA4 = ALT1, + MUX_PAD_CSI_DATA04__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA04__ECSPI1_SCLK = ALT3, + MUX_PAD_CSI_DATA04__EIM_AD04 = ALT4, + MUX_PAD_CSI_DATA04__GPIO4_IO25 = ALT5, + MUX_PAD_CSI_DATA04__SAI1_TX_SYNC = ALT6, + MUX_PAD_CSI_DATA04__USDHC1_WP = ALT8, + MUX_PAD_CSI_DATA04__ESAI_TX_FS = ALT9 +}; + +enum MUX_PAD_CSI_DATA05 { + MUX_PAD_CSI_DATA05__CSI_DATA07 = ALT0, + MUX_PAD_CSI_DATA05__USDHC2_DATA5 = ALT1, + MUX_PAD_CSI_DATA05__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA05__ECSPI1_SS0 = ALT3, + MUX_PAD_CSI_DATA05__EIM_AD05 = ALT4, + MUX_PAD_CSI_DATA05__GPIO4_IO26 = ALT5, + MUX_PAD_CSI_DATA05__SAI1_TX_BCLK = ALT6, + MUX_PAD_CSI_DATA05__USDHC1_CD_B = ALT8, + MUX_PAD_CSI_DATA05__ESAI_TX_CLK = ALT9 +}; + +enum MUX_PAD_CSI_DATA06 { + MUX_PAD_CSI_DATA06__CSI_DATA08 = ALT0, + MUX_PAD_CSI_DATA06__USDHC2_DATA6 = ALT1, + MUX_PAD_CSI_DATA06__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA06__ECSPI1_MOSI = ALT3, + MUX_PAD_CSI_DATA06__EIM_AD06 = ALT4, + MUX_PAD_CSI_DATA06__GPIO4_IO27 = ALT5, + MUX_PAD_CSI_DATA06__SAI1_RX_DATA = ALT6, + MUX_PAD_CSI_DATA06__USDHC1_RESET_B = ALT8, + MUX_PAD_CSI_DATA06__ESAI_TX5_RX0 = ALT9 +}; + +enum MUX_PAD_CSI_DATA07 { + MUX_PAD_CSI_DATA07__CSI_DATA09 = ALT0, + MUX_PAD_CSI_DATA07__USDHC2_DATA7 = ALT1, + MUX_PAD_CSI_DATA07__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_DATA07__ECSPI1_MISO = ALT3, + MUX_PAD_CSI_DATA07__EIM_AD07 = ALT4, + MUX_PAD_CSI_DATA07__GPIO4_IO28 = ALT5, + MUX_PAD_CSI_DATA07__SAI1_TX_DATA = ALT6, + MUX_PAD_CSI_DATA07__USDHC1_VSELECT = ALT8, + MUX_PAD_CSI_DATA07__ESAI_TX0 = ALT9 +}; + +enum MUX_PAD_CSI_HSYNC { + MUX_PAD_CSI_HSYNC__CSI_HSYNC = ALT0, + MUX_PAD_CSI_HSYNC__USDHC2_CMD = ALT1, + MUX_PAD_CSI_HSYNC__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_HSYNC__I2C2_SCL = ALT3, + MUX_PAD_CSI_HSYNC__EIM_LBA_B = ALT4, + MUX_PAD_CSI_HSYNC__GPIO4_IO20 = ALT5, + MUX_PAD_CSI_HSYNC__PWM8_OUT = ALT6, + MUX_PAD_CSI_HSYNC__UART6_CTS_B = ALT8, + MUX_PAD_CSI_HSYNC__ESAI_TX1 = ALT9 +}; + +enum MUX_PAD_CSI_MCLK { + MUX_PAD_CSI_MCLK__CSI_MCLK = ALT0, + MUX_PAD_CSI_MCLK__USDHC2_CD_B = ALT1, + MUX_PAD_CSI_MCLK__RAWNAND_CE2_B = ALT2, + MUX_PAD_CSI_MCLK__I2C1_SDA = ALT3, + MUX_PAD_CSI_MCLK__EIM_CS0_B = ALT4, + MUX_PAD_CSI_MCLK__GPIO4_IO17 = ALT5, + MUX_PAD_CSI_MCLK__SNVS_HP_VIO_5_CTL = ALT6, + MUX_PAD_CSI_MCLK__UART6_TX = ALT8, + MUX_PAD_CSI_MCLK__ESAI_TX3_RX2 = ALT9 +}; + +enum MUX_PAD_CSI_PIXCLK { + MUX_PAD_CSI_PIXCLK__CSI_PIXCLK = ALT0, + MUX_PAD_CSI_PIXCLK__USDHC2_WP = ALT1, + MUX_PAD_CSI_PIXCLK__RAWNAND_CE3_B = ALT2, + MUX_PAD_CSI_PIXCLK__I2C1_SCL = ALT3, + MUX_PAD_CSI_PIXCLK__EIM_OE = ALT4, + MUX_PAD_CSI_PIXCLK__GPIO4_IO18 = ALT5, + MUX_PAD_CSI_PIXCLK__SNVS_HP_VIO_5 = ALT6, + MUX_PAD_CSI_PIXCLK__UART6_RX = ALT8, + MUX_PAD_CSI_PIXCLK__ESAI_TX2_RX3 = ALT9 +}; + +enum MUX_PAD_CSI_VSYNC { + MUX_PAD_CSI_VSYNC__CSI_VSYNC = ALT0, + MUX_PAD_CSI_VSYNC__USDHC2_CLK = ALT1, + MUX_PAD_CSI_VSYNC__RESERVED_ALT2 = ALT2, + MUX_PAD_CSI_VSYNC__I2C2_SDA = ALT3, + MUX_PAD_CSI_VSYNC__EIM_RW = ALT4, + MUX_PAD_CSI_VSYNC__GPIO4_IO19 = ALT5, + MUX_PAD_CSI_VSYNC__PWM7_OUT = ALT6, + MUX_PAD_CSI_VSYNC__UART6_RTS_B = ALT8, + MUX_PAD_CSI_VSYNC__ESAI_TX4_RX1 = ALT9 +}; + +enum MUX_PAD_ENET1_RX_DATA0 { + MUX_PAD_ENET1_RX_DATA0__ENET1_RDATA00 = ALT0, + MUX_PAD_ENET1_RX_DATA0__UART4_RTS_B = ALT1, + MUX_PAD_ENET1_RX_DATA0__PWM1_OUT = ALT2, + MUX_PAD_ENET1_RX_DATA0__CSI_DATA16 = ALT3, + MUX_PAD_ENET1_RX_DATA0__FLEXCAN1_TX = ALT4, + MUX_PAD_ENET1_RX_DATA0__GPIO2_IO00 = ALT5, + MUX_PAD_ENET1_RX_DATA0__KPP_ROW00 = ALT6, + MUX_PAD_ENET1_RX_DATA0__USDHC1_LCTL = ALT8, + MUX_PAD_ENET1_RX_DATA0__EPDC_SDCE04 = ALT9 +}; + +enum MUX_PAD_ENET1_RX_DATA1 { + MUX_PAD_ENET1_RX_DATA1__ENET1_RDATA01 = ALT0, + MUX_PAD_ENET1_RX_DATA1__UART4_CTS_B = ALT1, + MUX_PAD_ENET1_RX_DATA1__PWM2_OUT = ALT2, + MUX_PAD_ENET1_RX_DATA1__CSI_DATA17 = ALT3, + MUX_PAD_ENET1_RX_DATA1__FLEXCAN1_RX = ALT4, + MUX_PAD_ENET1_RX_DATA1__GPIO2_IO01 = ALT5, + MUX_PAD_ENET1_RX_DATA1__KPP_COL00 = ALT6, + MUX_PAD_ENET1_RX_DATA1__USDHC2_LCTL = ALT8, + MUX_PAD_ENET1_RX_DATA1__EPDC_SDCE05 = ALT9 +}; + +enum MUX_PAD_ENET1_RX_EN { + MUX_PAD_ENET1_RX_EN__ENET1_RX_EN = ALT0, + MUX_PAD_ENET1_RX_EN__UART5_RTS_B = ALT1, + MUX_PAD_ENET1_RX_EN__CSI_DATA18 = ALT3, + MUX_PAD_ENET1_RX_EN__FLEXCAN2_TX = ALT4, + MUX_PAD_ENET1_RX_EN__GPIO2_IO02 = ALT5, + MUX_PAD_ENET1_RX_EN__KPP_ROW01 = ALT6, + MUX_PAD_ENET1_RX_EN__USDHC1_VSELECT = ALT8, + MUX_PAD_ENET1_RX_EN__EPDC_SDCE06 = ALT9 +}; + +enum MUX_PAD_ENET1_RX_ER { + MUX_PAD_ENET1_RX_ER__ENET1_RX_ER = ALT0, + MUX_PAD_ENET1_RX_ER__UART7_RTS_B = ALT1, + MUX_PAD_ENET1_RX_ER__PWM8_OUT = ALT2, + MUX_PAD_ENET1_RX_ER__CSI_DATA23 = ALT3, + MUX_PAD_ENET1_RX_ER__EIM_CRE = ALT4, + MUX_PAD_ENET1_RX_ER__GPIO2_IO07 = ALT5, + MUX_PAD_ENET1_RX_ER__KPP_COL03 = ALT6, + MUX_PAD_ENET1_RX_ER__GPT1_CAPTURE2 = ALT8, + MUX_PAD_ENET1_RX_ER__EPDC_SDOEZ = ALT9 +}; + +enum MUX_PAD_ENET1_TX_CLK { + MUX_PAD_ENET1_TX_CLK__ENET1_TX_CLK = ALT0, + MUX_PAD_ENET1_TX_CLK__UART7_CTS_B = ALT1, + MUX_PAD_ENET1_TX_CLK__PWM7_OUT = ALT2, + MUX_PAD_ENET1_TX_CLK__CSI_DATA22 = ALT3, + MUX_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 = ALT4, + MUX_PAD_ENET1_TX_CLK__GPIO2_IO06 = ALT5, + MUX_PAD_ENET1_TX_CLK__KPP_ROW03 = ALT6, + MUX_PAD_ENET1_TX_CLK__GPT1_CLK = ALT8, + MUX_PAD_ENET1_TX_CLK__EPDC_SDOED = ALT9 +}; + +enum MUX_PAD_ENET1_TX_DATA0 { + MUX_PAD_ENET1_TX_DATA0__ENET1_TDATA00 = ALT0, + MUX_PAD_ENET1_TX_DATA0__UART5_CTS_B = ALT1, + MUX_PAD_ENET1_TX_DATA0__CSI_DATA19 = ALT3, + MUX_PAD_ENET1_TX_DATA0__FLEXCAN2_RX = ALT4, + MUX_PAD_ENET1_TX_DATA0__GPIO2_IO03 = ALT5, + MUX_PAD_ENET1_TX_DATA0__KPP_COL01 = ALT6, + MUX_PAD_ENET1_TX_DATA0__USDHC2_VSELECT = ALT8, + MUX_PAD_ENET1_TX_DATA0__EPDC_SDCE07 = ALT9 +}; + +enum MUX_PAD_ENET1_TX_DATA1 { + MUX_PAD_ENET1_TX_DATA1__ENET1_TDATA01 = ALT0, + MUX_PAD_ENET1_TX_DATA1__UART6_CTS_B = ALT1, + MUX_PAD_ENET1_TX_DATA1__PWM5_OUT = ALT2, + MUX_PAD_ENET1_TX_DATA1__CSI_DATA20 = ALT3, + MUX_PAD_ENET1_TX_DATA1__ENET2_MDIO = ALT4, + MUX_PAD_ENET1_TX_DATA1__GPIO2_IO04 = ALT5, + MUX_PAD_ENET1_TX_DATA1__KPP_ROW02 = ALT6, + MUX_PAD_ENET1_TX_DATA1__WDOG1_WDOG_RST_B_DEB = ALT8, + MUX_PAD_ENET1_TX_DATA1__EPDC_SDCE08 = ALT9 +}; + +enum MUX_PAD_ENET1_TX_EN { + MUX_PAD_ENET1_TX_EN__ENET1_TX_EN = ALT0, + MUX_PAD_ENET1_TX_EN__UART6_RTS_B = ALT1, + MUX_PAD_ENET1_TX_EN__PWM6_OUT = ALT2, + MUX_PAD_ENET1_TX_EN__CSI_DATA21 = ALT3, + MUX_PAD_ENET1_TX_EN__ENET2_MDC = ALT4, + MUX_PAD_ENET1_TX_EN__GPIO2_IO05 = ALT5, + MUX_PAD_ENET1_TX_EN__KPP_COL02 = ALT6, + MUX_PAD_ENET1_TX_EN__WDOG2_WDOG_RST_B_DEB = ALT8, + MUX_PAD_ENET1_TX_EN__EPDC_SDCE09 = ALT9 +}; + +enum MUX_PAD_ENET2_RX_DATA0 { + MUX_PAD_ENET2_RX_DATA0__ENET2_RDATA00 = ALT0, + MUX_PAD_ENET2_RX_DATA0__UART6_TX = ALT1, + MUX_PAD_ENET2_RX_DATA0__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_RX_DATA0__I2C3_SCL = ALT3, + MUX_PAD_ENET2_RX_DATA0__ENET1_MDIO = ALT4, + MUX_PAD_ENET2_RX_DATA0__GPIO2_IO08 = ALT5, + MUX_PAD_ENET2_RX_DATA0__KPP_ROW04 = ALT6, + MUX_PAD_ENET2_RX_DATA0__USB_OTG1_PWR = ALT8, + MUX_PAD_ENET2_RX_DATA0__EPDC_SDDO08 = ALT9 +}; + +enum MUX_PAD_ENET2_RX_DATA1 { + MUX_PAD_ENET2_RX_DATA1__ENET2_RDATA01 = ALT0, + MUX_PAD_ENET2_RX_DATA1__UART6_RX = ALT1, + MUX_PAD_ENET2_RX_DATA1__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_RX_DATA1__I2C3_SDA = ALT3, + MUX_PAD_ENET2_RX_DATA1__ENET1_MDC = ALT4, + MUX_PAD_ENET2_RX_DATA1__GPIO2_IO09 = ALT5, + MUX_PAD_ENET2_RX_DATA1__KPP_COL04 = ALT6, + MUX_PAD_ENET2_RX_DATA1__USB_OTG1_OC = ALT8, + MUX_PAD_ENET2_RX_DATA1__EPDC_SDDO09 = ALT9 +}; + +enum MUX_PAD_ENET2_RX_EN { + MUX_PAD_ENET2_RX_EN__ENET2_RX_EN = ALT0, + MUX_PAD_ENET2_RX_EN__UART7_TX = ALT1, + MUX_PAD_ENET2_RX_EN__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_RX_EN__I2C4_SCL = ALT3, + MUX_PAD_ENET2_RX_EN__EIM_ADDR26 = ALT4, + MUX_PAD_ENET2_RX_EN__GPIO2_IO10 = ALT5, + MUX_PAD_ENET2_RX_EN__KPP_ROW05 = ALT6, + MUX_PAD_ENET2_RX_EN__ENET1_REF_CLK_25M = ALT8, + MUX_PAD_ENET2_RX_EN__EPDC_SDDO10 = ALT9 +}; + +enum MUX_PAD_ENET2_RX_ER { + MUX_PAD_ENET2_RX_ER__ENET2_RX_ER = ALT0, + MUX_PAD_ENET2_RX_ER__UART8_RTS_B = ALT1, + MUX_PAD_ENET2_RX_ER__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_RX_ER__ECSPI4_SS0 = ALT3, + MUX_PAD_ENET2_RX_ER__EIM_ADDR25 = ALT4, + MUX_PAD_ENET2_RX_ER__GPIO2_IO15 = ALT5, + MUX_PAD_ENET2_RX_ER__KPP_COL07 = ALT6, + MUX_PAD_ENET2_RX_ER__WDOG1_WDOG_ANY = ALT8, + MUX_PAD_ENET2_RX_ER__EPDC_SDDO15 = ALT9 +}; + +enum MUX_PAD_ENET2_TX_CLK { + MUX_PAD_ENET2_TX_CLK__ENET2_TX_CLK = ALT0, + MUX_PAD_ENET2_TX_CLK__UART8_CTS_B = ALT1, + MUX_PAD_ENET2_TX_CLK__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_TX_CLK__ECSPI4_MISO = ALT3, + MUX_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 = ALT4, + MUX_PAD_ENET2_TX_CLK__GPIO2_IO14 = ALT5, + MUX_PAD_ENET2_TX_CLK__KPP_ROW07 = ALT6, + MUX_PAD_ENET2_TX_CLK__ANATOP_OTG2_ID = ALT8, + MUX_PAD_ENET2_TX_CLK__EPDC_SDDO14 = ALT9 +}; + +enum MUX_PAD_ENET2_TX_DATA0 { + MUX_PAD_ENET2_TX_DATA0__ENET2_TDATA00 = ALT0, + MUX_PAD_ENET2_TX_DATA0__UART7_RX = ALT1, + MUX_PAD_ENET2_TX_DATA0__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_TX_DATA0__I2C4_SDA = ALT3, + MUX_PAD_ENET2_TX_DATA0__EIM_EB_B02 = ALT4, + MUX_PAD_ENET2_TX_DATA0__GPIO2_IO11 = ALT5, + MUX_PAD_ENET2_TX_DATA0__KPP_COL05 = ALT6, + MUX_PAD_ENET2_TX_DATA0__EPDC_SDDO11 = ALT9 +}; + +enum MUX_PAD_ENET2_TX_DATA1 { + MUX_PAD_ENET2_TX_DATA1__ENET2_TDATA01 = ALT0, + MUX_PAD_ENET2_TX_DATA1__UART8_TX = ALT1, + MUX_PAD_ENET2_TX_DATA1__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_TX_DATA1__ECSPI4_SCLK = ALT3, + MUX_PAD_ENET2_TX_DATA1__EIM_EB_B03 = ALT4, + MUX_PAD_ENET2_TX_DATA1__GPIO2_IO12 = ALT5, + MUX_PAD_ENET2_TX_DATA1__KPP_ROW06 = ALT6, + MUX_PAD_ENET2_TX_DATA1__USB_OTG2_PWR = ALT8, + MUX_PAD_ENET2_TX_DATA1__EPDC_SDDO12 = ALT9 +}; + +enum MUX_PAD_ENET2_TX_EN { + MUX_PAD_ENET2_TX_EN__ENET2_TX_EN = ALT0, + MUX_PAD_ENET2_TX_EN__UART8_RX = ALT1, + MUX_PAD_ENET2_TX_EN__RESERVED_ALT2 = ALT2, + MUX_PAD_ENET2_TX_EN__ECSPI4_MOSI = ALT3, + MUX_PAD_ENET2_TX_EN__EIM_ACLK_FREERUN = ALT4, + MUX_PAD_ENET2_TX_EN__GPIO2_IO13 = ALT5, + MUX_PAD_ENET2_TX_EN__KPP_COL06 = ALT6, + MUX_PAD_ENET2_TX_EN__USB_OTG2_OC = ALT8, + MUX_PAD_ENET2_TX_EN__EPDC_SDDO13 = ALT9 +}; + +enum MUX_PAD_GPIO1_IO00 { + MUX_PAD_GPIO1_IO00__I2C2_SCL = ALT0, + MUX_PAD_GPIO1_IO00__GPT1_CAPTURE1 = ALT1, + MUX_PAD_GPIO1_IO00__ANATOP_OTG1_ID = ALT2, + MUX_PAD_GPIO1_IO00__ENET1_REF_CLK1 = ALT3, + MUX_PAD_GPIO1_IO00__MQS_RIGHT = ALT4, + MUX_PAD_GPIO1_IO00__GPIO1_IO00 = ALT5, + MUX_PAD_GPIO1_IO00__ENET1_1588_EVENT0_IN = ALT6, + MUX_PAD_GPIO1_IO00__SRC_SYSTEM_RESET = ALT7, + MUX_PAD_GPIO1_IO00__WDOG3_WDOG_B = ALT8 +}; + +enum MUX_PAD_GPIO1_IO02 { + MUX_PAD_GPIO1_IO02__I2C1_SCL = ALT0, + MUX_PAD_GPIO1_IO02__GPT1_COMPARE2 = ALT1, + MUX_PAD_GPIO1_IO02__USB_OTG2_PWR = ALT2, + MUX_PAD_GPIO1_IO02__ENET1_REF_CLK_25M = ALT3, + MUX_PAD_GPIO1_IO02__USDHC1_WP = ALT4, + MUX_PAD_GPIO1_IO02__GPIO1_IO02 = ALT5 +}; + +enum MUX_PAD_GPIO1_IO03 { + MUX_PAD_GPIO1_IO03__I2C1_SDA = ALT0, + MUX_PAD_GPIO1_IO03__GPT1_COMPARE3 = ALT1, + MUX_PAD_GPIO1_IO03__USB_OTG2_OC = ALT2, + MUX_PAD_GPIO1_IO03__USDHC1_CD_B = ALT4, + MUX_PAD_GPIO1_IO03__GPIO1_IO03 = ALT5, + MUX_PAD_GPIO1_IO03__CCM_DI0_EXT_CLK = ALT6, + MUX_PAD_GPIO1_IO03__SRC_TESTER_ACK = ALT7, + MUX_PAD_GPIO1_IO03__UART1_RX = ALT8 +}; + +enum MUX_PAD_GPIO1_IO04 { + MUX_PAD_GPIO1_IO04__ENET1_REF_CLK1 = ALT0, + MUX_PAD_GPIO1_IO04__PWM3_OUT = ALT1, + MUX_PAD_GPIO1_IO04__USB_OTG1_PWR = ALT2, + MUX_PAD_GPIO1_IO04__USDHC1_RESET_B = ALT4, + MUX_PAD_GPIO1_IO04__GPIO1_IO04 = ALT5, + MUX_PAD_GPIO1_IO04__ENET2_1588_EVENT0_IN = ALT6, + MUX_PAD_GPIO1_IO04__UART5_TX = ALT8 +}; + +enum MUX_PAD_GPIO1_IO05 { + MUX_PAD_GPIO1_IO05__ENET2_REF_CLK2 = ALT0, + MUX_PAD_GPIO1_IO05__PWM4_OUT = ALT1, + MUX_PAD_GPIO1_IO05__ANATOP_OTG2_ID = ALT2, + MUX_PAD_GPIO1_IO05__CSI_FIELD = ALT3, + MUX_PAD_GPIO1_IO05__USDHC1_VSELECT = ALT4, + MUX_PAD_GPIO1_IO05__GPIO1_IO05 = ALT5, + MUX_PAD_GPIO1_IO05__ENET2_1588_EVENT0_OUT = ALT6, + MUX_PAD_GPIO1_IO05__UART5_RX = ALT8 +}; + +enum MUX_PAD_GPIO1_IO06 { + MUX_PAD_GPIO1_IO06__ENET1_MDIO = ALT0, + MUX_PAD_GPIO1_IO06__ENET2_MDIO = ALT1, + MUX_PAD_GPIO1_IO06__USB_OTG_PWR_WAKE = ALT2, + MUX_PAD_GPIO1_IO06__CSI_MCLK = ALT3, + MUX_PAD_GPIO1_IO06__USDHC2_WP = ALT4, + MUX_PAD_GPIO1_IO06__GPIO1_IO06 = ALT5, + MUX_PAD_GPIO1_IO06__CCM_WAIT = ALT6, + MUX_PAD_GPIO1_IO06__CCM_REF_EN_B = ALT7, + MUX_PAD_GPIO1_IO06__UART1_CTS_B = ALT8 +}; + +enum MUX_PAD_GPIO1_IO07 { + MUX_PAD_GPIO1_IO07__ENET1_MDC = ALT0, + MUX_PAD_GPIO1_IO07__ENET2_MDC = ALT1, + MUX_PAD_GPIO1_IO07__USB_OTG_HOST_MODE = ALT2, + MUX_PAD_GPIO1_IO07__CSI_PIXCLK = ALT3, + MUX_PAD_GPIO1_IO07__USDHC2_CD_B = ALT4, + MUX_PAD_GPIO1_IO07__GPIO1_IO07 = ALT5, + MUX_PAD_GPIO1_IO07__CCM_STOP = ALT6, + MUX_PAD_GPIO1_IO07__UART1_RTS_B = ALT8 +}; + +enum MUX_PAD_GPIO1_IO08 { + MUX_PAD_GPIO1_IO08__PWM1_OUT = ALT0, + MUX_PAD_GPIO1_IO08__WDOG1_WDOG_B = ALT1, + MUX_PAD_GPIO1_IO08__SPDIF_OUT = ALT2, + MUX_PAD_GPIO1_IO08__CSI_VSYNC = ALT3, + MUX_PAD_GPIO1_IO08__USDHC2_VSELECT = ALT4, + MUX_PAD_GPIO1_IO08__GPIO1_IO08 = ALT5, + MUX_PAD_GPIO1_IO08__CCM_PMIC_RDY = ALT6, + MUX_PAD_GPIO1_IO08__UART5_RTS_B = ALT8 +}; + +enum MUX_PAD_GPIO1_IO09 { + MUX_PAD_GPIO1_IO09__PWM2_OUT = ALT0, + MUX_PAD_GPIO1_IO09__WDOG1_WDOG_ANY = ALT1, + MUX_PAD_GPIO1_IO09__SPDIF_IN = ALT2, + MUX_PAD_GPIO1_IO09__CSI_HSYNC = ALT3, + MUX_PAD_GPIO1_IO09__USDHC2_RESET_B = ALT4, + MUX_PAD_GPIO1_IO09__GPIO1_IO09 = ALT5, + MUX_PAD_GPIO1_IO09__USDHC1_RESET_B = ALT6, + MUX_PAD_GPIO1_IO09__UART5_CTS_B = ALT8 +}; + +enum MUX_PAD_JTAG_TCK { + MUX_PAD_JTAG_TCK__SJC_TCK = ALT0, + MUX_PAD_JTAG_TCK__GPT2_COMPARE2 = ALT1, + MUX_PAD_JTAG_TCK__SAI2_RX_DATA = ALT2, + MUX_PAD_JTAG_TCK__PWM7_OUT = ALT4, + MUX_PAD_JTAG_TCK__GPIO1_IO14 = ALT5, + MUX_PAD_JTAG_TCK__SIM2_POWER_FAIL = ALT8 +}; + +enum MUX_PAD_JTAG_TDO { + MUX_PAD_JTAG_TDO__SJC_TDO = ALT0, + MUX_PAD_JTAG_TDO__GPT2_CAPTURE2 = ALT1, + MUX_PAD_JTAG_TDO__SAI2_TX_SYNC = ALT2, + MUX_PAD_JTAG_TDO__CCM_CLKO2 = ALT3, + MUX_PAD_JTAG_TDO__CCM_STOP = ALT4, + MUX_PAD_JTAG_TDO__GPIO1_IO12 = ALT5, + MUX_PAD_JTAG_TDO__MQS_RIGHT = ALT6, + MUX_PAD_JTAG_TDO__EPIT2_OUT = ALT8 +}; + +enum MUX_PAD_JTAG_TMS { + MUX_PAD_JTAG_TMS__SJC_TMS = ALT0, + MUX_PAD_JTAG_TMS__GPT2_CAPTURE1 = ALT1, + MUX_PAD_JTAG_TMS__SAI2_MCLK = ALT2, + MUX_PAD_JTAG_TMS__CCM_CLKO1 = ALT3, + MUX_PAD_JTAG_TMS__CCM_WAIT = ALT4, + MUX_PAD_JTAG_TMS__GPIO1_IO11 = ALT5, + MUX_PAD_JTAG_TMS__SDMA_EXT_EVENT01 = ALT6, + MUX_PAD_JTAG_TMS__EPIT1_OUT = ALT8 +}; + +enum MUX_PAD_JTAG_TRST_B { + MUX_PAD_JTAG_TRST_B__SJC_TRSTB = ALT0, + MUX_PAD_JTAG_TRST_B__GPT2_COMPARE3 = ALT1, + MUX_PAD_JTAG_TRST_B__SAI2_TX_DATA = ALT2, + MUX_PAD_JTAG_TRST_B__PWM8_OUT = ALT4, + MUX_PAD_JTAG_TRST_B__GPIO1_IO15 = ALT5, + MUX_PAD_JTAG_TRST_B__CAAM_RNG_OSC_OBS = ALT8 +}; + +enum MUX_PAD_LCD_CLK { + MUX_PAD_LCD_CLK__LCDIF_CLK = ALT0, + MUX_PAD_LCD_CLK__LCDIF_WR_RWN = ALT1, + MUX_PAD_LCD_CLK__UART4_TX = ALT2, + MUX_PAD_LCD_CLK__SAI3_MCLK = ALT3, + MUX_PAD_LCD_CLK__EIM_CS2_B = ALT4, + MUX_PAD_LCD_CLK__GPIO3_IO00 = ALT5, + MUX_PAD_LCD_CLK__WDOG1_WDOG_RST_B_DEB = ALT8, + MUX_PAD_LCD_CLK__EPDC_SDCLK = ALT9 +}; + +enum MUX_PAD_LCD_DATA00 { + MUX_PAD_LCD_DATA00__LCDIF_DATA00 = ALT0, + MUX_PAD_LCD_DATA00__PWM1_OUT = ALT1, + MUX_PAD_LCD_DATA00__ENET1_1588_EVENT2_IN = ALT3, + MUX_PAD_LCD_DATA00__I2C3_SDA = ALT4, + MUX_PAD_LCD_DATA00__GPIO3_IO05 = ALT5, + MUX_PAD_LCD_DATA00__SRC_BT_CFG00 = ALT6, + MUX_PAD_LCD_DATA00__SAI1_MCLK = ALT8, + MUX_PAD_LCD_DATA00__EPDC_SDDO00 = ALT9 +}; + +enum MUX_PAD_LCD_DATA01 { + MUX_PAD_LCD_DATA01__LCDIF_DATA01 = ALT0, + MUX_PAD_LCD_DATA01__PWM2_OUT = ALT1, + MUX_PAD_LCD_DATA01__ENET1_1588_EVENT2_OUT = ALT3, + MUX_PAD_LCD_DATA01__I2C3_SCL = ALT4, + MUX_PAD_LCD_DATA01__GPIO3_IO06 = ALT5, + MUX_PAD_LCD_DATA01__SRC_BT_CFG01 = ALT6, + MUX_PAD_LCD_DATA01__SAI1_TX_SYNC = ALT8, + MUX_PAD_LCD_DATA01__EPDC_SDDO01 = ALT9 +}; + +enum MUX_PAD_LCD_DATA02 { + MUX_PAD_LCD_DATA02__LCDIF_DATA02 = ALT0, + MUX_PAD_LCD_DATA02__PWM3_OUT = ALT1, + MUX_PAD_LCD_DATA02__ENET1_1588_EVENT3_IN = ALT3, + MUX_PAD_LCD_DATA02__I2C4_SDA = ALT4, + MUX_PAD_LCD_DATA02__GPIO3_IO07 = ALT5, + MUX_PAD_LCD_DATA02__SRC_BT_CFG02 = ALT6, + MUX_PAD_LCD_DATA02__SAI1_TX_BCLK = ALT8, + MUX_PAD_LCD_DATA02__EPDC_SDDO02 = ALT9 +}; + +enum MUX_PAD_LCD_DATA03 { + MUX_PAD_LCD_DATA03__LCDIF_DATA03 = ALT0, + MUX_PAD_LCD_DATA03__PWM4_OUT = ALT1, + MUX_PAD_LCD_DATA03__ENET1_1588_EVENT3_OUT = ALT3, + MUX_PAD_LCD_DATA03__I2C4_SCL = ALT4, + MUX_PAD_LCD_DATA03__GPIO3_IO08 = ALT5, + MUX_PAD_LCD_DATA03__SRC_BT_CFG03 = ALT6, + MUX_PAD_LCD_DATA03__SAI1_RX_DATA = ALT8, + MUX_PAD_LCD_DATA03__EPDC_SDDO03 = ALT9 +}; + +enum MUX_PAD_LCD_DATA04 { + MUX_PAD_LCD_DATA04__LCDIF_DATA04 = ALT0, + MUX_PAD_LCD_DATA04__UART8_CTS_B = ALT1, + MUX_PAD_LCD_DATA04__ENET2_1588_EVENT2_IN = ALT3, + MUX_PAD_LCD_DATA04__SPDIF_SR_CLK = ALT4, + MUX_PAD_LCD_DATA04__GPIO3_IO09 = ALT5, + MUX_PAD_LCD_DATA04__SRC_BT_CFG04 = ALT6, + MUX_PAD_LCD_DATA04__SAI1_TX_DATA = ALT8, + MUX_PAD_LCD_DATA04__EPDC_SDDO04 = ALT9 +}; + +enum MUX_PAD_LCD_DATA05 { + MUX_PAD_LCD_DATA05__LCDIF_DATA05 = ALT0, + MUX_PAD_LCD_DATA05__UART8_RTS_B = ALT1, + MUX_PAD_LCD_DATA05__ENET2_1588_EVENT2_OUT = ALT3, + MUX_PAD_LCD_DATA05__SPDIF_OUT = ALT4, + MUX_PAD_LCD_DATA05__GPIO3_IO10 = ALT5, + MUX_PAD_LCD_DATA05__SRC_BT_CFG05 = ALT6, + MUX_PAD_LCD_DATA05__ECSPI1_SS1 = ALT8, + MUX_PAD_LCD_DATA05__EPDC_SDDO05 = ALT9 +}; + +enum MUX_PAD_LCD_DATA06 { + MUX_PAD_LCD_DATA06__LCDIF_DATA06 = ALT0, + MUX_PAD_LCD_DATA06__UART7_CTS_B = ALT1, + MUX_PAD_LCD_DATA06__ENET2_1588_EVENT3_IN = ALT3, + MUX_PAD_LCD_DATA06__SPDIF_LOCK = ALT4, + MUX_PAD_LCD_DATA06__GPIO3_IO11 = ALT5, + MUX_PAD_LCD_DATA06__SRC_BT_CFG06 = ALT6, + MUX_PAD_LCD_DATA06__ECSPI1_SS2 = ALT8, + MUX_PAD_LCD_DATA06__EPDC_SDDO06 = ALT9 +}; + +enum MUX_PAD_LCD_DATA07 { + MUX_PAD_LCD_DATA07__LCDIF_DATA07 = ALT0, + MUX_PAD_LCD_DATA07__UART7_RTS_B = ALT1, + MUX_PAD_LCD_DATA07__ENET2_1588_EVENT3_OUT = ALT3, + MUX_PAD_LCD_DATA07__SPDIF_EXT_CLK = ALT4, + MUX_PAD_LCD_DATA07__GPIO3_IO12 = ALT5, + MUX_PAD_LCD_DATA07__SRC_BT_CFG07 = ALT6, + MUX_PAD_LCD_DATA07__ECSPI1_SS3 = ALT8, + MUX_PAD_LCD_DATA07__EPDC_SDDO07 = ALT9 +}; + +enum MUX_PAD_LCD_DATA08 { + MUX_PAD_LCD_DATA08__LCDIF_DATA08 = ALT0, + MUX_PAD_LCD_DATA08__SPDIF_IN = ALT1, + MUX_PAD_LCD_DATA08__CSI_DATA16 = ALT3, + MUX_PAD_LCD_DATA08__EIM_DATA00 = ALT4, + MUX_PAD_LCD_DATA08__GPIO3_IO13 = ALT5, + MUX_PAD_LCD_DATA08__SRC_BT_CFG08 = ALT6, + MUX_PAD_LCD_DATA08__FLEXCAN1_TX = ALT8, + MUX_PAD_LCD_DATA08__EPDC_PWRIRQ = ALT9 +}; + +enum MUX_PAD_LCD_DATA09 { + MUX_PAD_LCD_DATA09__LCDIF_DATA09 = ALT0, + MUX_PAD_LCD_DATA09__SAI3_MCLK = ALT1, + MUX_PAD_LCD_DATA09__CSI_DATA17 = ALT3, + MUX_PAD_LCD_DATA09__EIM_DATA01 = ALT4, + MUX_PAD_LCD_DATA09__GPIO3_IO14 = ALT5, + MUX_PAD_LCD_DATA09__SRC_BT_CFG09 = ALT6, + MUX_PAD_LCD_DATA09__FLEXCAN1_RX = ALT8, + MUX_PAD_LCD_DATA09__EPDC_PWRWAKE = ALT9 +}; + +enum MUX_PAD_LCD_DATA10 { + MUX_PAD_LCD_DATA10__LCDIF_DATA10 = ALT0, + MUX_PAD_LCD_DATA10__SAI3_RX_SYNC = ALT1, + MUX_PAD_LCD_DATA10__CSI_DATA18 = ALT3, + MUX_PAD_LCD_DATA10__EIM_DATA02 = ALT4, + MUX_PAD_LCD_DATA10__GPIO3_IO15 = ALT5, + MUX_PAD_LCD_DATA10__SRC_BT_CFG10 = ALT6, + MUX_PAD_LCD_DATA10__FLEXCAN2_TX = ALT8, + MUX_PAD_LCD_DATA10__EPDC_PWRCOM = ALT9 +}; + +enum MUX_PAD_LCD_DATA11 { + MUX_PAD_LCD_DATA11__LCDIF_DATA11 = ALT0, + MUX_PAD_LCD_DATA11__SAI3_RX_BCLK = ALT1, + MUX_PAD_LCD_DATA11__CSI_DATA19 = ALT3, + MUX_PAD_LCD_DATA11__EIM_DATA03 = ALT4, + MUX_PAD_LCD_DATA11__GPIO3_IO16 = ALT5, + MUX_PAD_LCD_DATA11__SRC_BT_CFG11 = ALT6, + MUX_PAD_LCD_DATA11__FLEXCAN2_RX = ALT8, + MUX_PAD_LCD_DATA11__EPDC_PWRSTAT = ALT9 +}; + +enum MUX_PAD_LCD_DATA12 { + MUX_PAD_LCD_DATA12__LCDIF_DATA12 = ALT0, + MUX_PAD_LCD_DATA12__SAI3_TX_SYNC = ALT1, + MUX_PAD_LCD_DATA12__CSI_DATA20 = ALT3, + MUX_PAD_LCD_DATA12__EIM_DATA04 = ALT4, + MUX_PAD_LCD_DATA12__GPIO3_IO17 = ALT5, + MUX_PAD_LCD_DATA12__SRC_BT_CFG12 = ALT6, + MUX_PAD_LCD_DATA12__ECSPI1_RDY = ALT8, + MUX_PAD_LCD_DATA12__EPDC_PWRCTRL00 = ALT9 +}; + +enum MUX_PAD_LCD_DATA13 { + MUX_PAD_LCD_DATA13__LCDIF_DATA13 = ALT0, + MUX_PAD_LCD_DATA13__SAI3_TX_BCLK = ALT1, + MUX_PAD_LCD_DATA13__CSI_DATA21 = ALT3, + MUX_PAD_LCD_DATA13__EIM_DATA05 = ALT4, + MUX_PAD_LCD_DATA13__GPIO3_IO18 = ALT5, + MUX_PAD_LCD_DATA13__SRC_BT_CFG13 = ALT6, + MUX_PAD_LCD_DATA13__USDHC2_RESET_B = ALT8, + MUX_PAD_LCD_DATA13__EPDC_BDR00 = ALT9 +}; + +enum MUX_PAD_LCD_DATA14 { + MUX_PAD_LCD_DATA14__LCDIF_DATA14 = ALT0, + MUX_PAD_LCD_DATA14__SAI3_RX_DATA = ALT1, + MUX_PAD_LCD_DATA14__CSI_DATA22 = ALT3, + MUX_PAD_LCD_DATA14__EIM_DATA06 = ALT4, + MUX_PAD_LCD_DATA14__GPIO3_IO19 = ALT5, + MUX_PAD_LCD_DATA14__SRC_BT_CFG14 = ALT6, + MUX_PAD_LCD_DATA14__USDHC2_DATA4 = ALT8, + MUX_PAD_LCD_DATA14__EPDC_SDSHR = ALT9 +}; + +enum MUX_PAD_LCD_DATA15 { + MUX_PAD_LCD_DATA15__LCDIF_DATA15 = ALT0, + MUX_PAD_LCD_DATA15__SAI3_TX_DATA = ALT1, + MUX_PAD_LCD_DATA15__CSI_DATA23 = ALT3, + MUX_PAD_LCD_DATA15__EIM_DATA07 = ALT4, + MUX_PAD_LCD_DATA15__GPIO3_IO20 = ALT5, + MUX_PAD_LCD_DATA15__SRC_BT_CFG15 = ALT6, + MUX_PAD_LCD_DATA15__USDHC2_DATA5 = ALT8, + MUX_PAD_LCD_DATA15__EPDC_GDRL = ALT9 +}; + +enum MUX_PAD_LCD_DATA16 { + MUX_PAD_LCD_DATA16__LCDIF_DATA16 = ALT0, + MUX_PAD_LCD_DATA16__UART7_TX = ALT1, + MUX_PAD_LCD_DATA16__CSI_DATA01 = ALT3, + MUX_PAD_LCD_DATA16__EIM_DATA08 = ALT4, + MUX_PAD_LCD_DATA16__GPIO3_IO21 = ALT5, + MUX_PAD_LCD_DATA16__SRC_BT_CFG24 = ALT6, + MUX_PAD_LCD_DATA16__USDHC2_DATA6 = ALT8, + MUX_PAD_LCD_DATA16__EPDC_GDCLK = ALT9 +}; + +enum MUX_PAD_LCD_DATA17 { + MUX_PAD_LCD_DATA17__LCDIF_DATA17 = ALT0, + MUX_PAD_LCD_DATA17__UART7_RX = ALT1, + MUX_PAD_LCD_DATA17__CSI_DATA00 = ALT3, + MUX_PAD_LCD_DATA17__EIM_DATA09 = ALT4, + MUX_PAD_LCD_DATA17__GPIO3_IO22 = ALT5, + MUX_PAD_LCD_DATA17__SRC_BT_CFG25 = ALT6, + MUX_PAD_LCD_DATA17__USDHC2_DATA7 = ALT8, + MUX_PAD_LCD_DATA17__EPDC_GDSP = ALT9 +}; + +enum MUX_PAD_LCD_DATA18 { + MUX_PAD_LCD_DATA18__LCDIF_DATA18 = ALT0, + MUX_PAD_LCD_DATA18__PWM5_OUT = ALT1, + MUX_PAD_LCD_DATA18__CA7_MX6ULL_EVENTO = ALT2, + MUX_PAD_LCD_DATA18__CSI_DATA10 = ALT3, + MUX_PAD_LCD_DATA18__EIM_DATA10 = ALT4, + MUX_PAD_LCD_DATA18__GPIO3_IO23 = ALT5, + MUX_PAD_LCD_DATA18__SRC_BT_CFG26 = ALT6, + MUX_PAD_LCD_DATA18__USDHC2_CMD = ALT8, + MUX_PAD_LCD_DATA18__EPDC_BDR01 = ALT9 +}; + +enum MUX_PAD_LCD_DATA19 { + MUX_PAD_LCD_DATA19__EIM_DATA11 = ALT4, + MUX_PAD_LCD_DATA19__GPIO3_IO24 = ALT5, + MUX_PAD_LCD_DATA19__SRC_BT_CFG27 = ALT6, + MUX_PAD_LCD_DATA19__USDHC2_CLK = ALT8, + MUX_PAD_LCD_DATA19__EPDC_VCOM00 = ALT9, + MUX_PAD_LCD_DATA19__LCDIF_DATA19 = ALT0, + MUX_PAD_LCD_DATA19__PWM6_OUT = ALT1, + MUX_PAD_LCD_DATA19__WDOG1_WDOG_ANY = ALT2, + MUX_PAD_LCD_DATA19__CSI_DATA11 = ALT3 +}; + +enum MUX_PAD_LCD_DATA20 { + MUX_PAD_LCD_DATA20__EIM_DATA12 = ALT4, + MUX_PAD_LCD_DATA20__GPIO3_IO25 = ALT5, + MUX_PAD_LCD_DATA20__SRC_BT_CFG28 = ALT6, + MUX_PAD_LCD_DATA20__USDHC2_DATA0 = ALT8, + MUX_PAD_LCD_DATA20__EPDC_VCOM01 = ALT9, + MUX_PAD_LCD_DATA20__LCDIF_DATA20 = ALT0, + MUX_PAD_LCD_DATA20__UART8_TX = ALT1, + MUX_PAD_LCD_DATA20__ECSPI1_SCLK = ALT2, + MUX_PAD_LCD_DATA20__CSI_DATA12 = ALT3 +}; + +enum MUX_PAD_LCD_DATA21 { + MUX_PAD_LCD_DATA21__LCDIF_DATA21 = ALT0, + MUX_PAD_LCD_DATA21__UART8_RX = ALT1, + MUX_PAD_LCD_DATA21__ECSPI1_SS0 = ALT2, + MUX_PAD_LCD_DATA21__CSI_DATA13 = ALT3, + MUX_PAD_LCD_DATA21__EIM_DATA13 = ALT4, + MUX_PAD_LCD_DATA21__GPIO3_IO26 = ALT5, + MUX_PAD_LCD_DATA21__SRC_BT_CFG29 = ALT6, + MUX_PAD_LCD_DATA21__USDHC2_DATA1 = ALT8, + MUX_PAD_LCD_DATA21__EPDC_SDCE01 = ALT9 +}; + +enum MUX_PAD_LCD_DATA22 { + MUX_PAD_LCD_DATA22__LCDIF_DATA22 = ALT0, + MUX_PAD_LCD_DATA22__MQS_RIGHT = ALT1, + MUX_PAD_LCD_DATA22__ECSPI1_MOSI = ALT2, + MUX_PAD_LCD_DATA22__CSI_DATA14 = ALT3, + MUX_PAD_LCD_DATA22__EIM_DATA14 = ALT4, + MUX_PAD_LCD_DATA22__GPIO3_IO27 = ALT5, + MUX_PAD_LCD_DATA22__SRC_BT_CFG30 = ALT6, + MUX_PAD_LCD_DATA22__USDHC2_DATA2 = ALT8, + MUX_PAD_LCD_DATA22__EPDC_SDCE02 = ALT9 +}; + +enum MUX_PAD_LCD_DATA23 { + MUX_PAD_LCD_DATA23__EPDC_SDCE03 = ALT9, + MUX_PAD_LCD_DATA23__LCDIF_DATA23 = ALT0, + MUX_PAD_LCD_DATA23__MQS_LEFT = ALT1, + MUX_PAD_LCD_DATA23__ECSPI1_MISO = ALT2, + MUX_PAD_LCD_DATA23__CSI_DATA15 = ALT3, + MUX_PAD_LCD_DATA23__EIM_DATA15 = ALT4, + MUX_PAD_LCD_DATA23__GPIO3_IO28 = ALT5, + MUX_PAD_LCD_DATA23__SRC_BT_CFG31 = ALT6, + MUX_PAD_LCD_DATA23__USDHC2_DATA3 = ALT8 +}; + +enum MUX_PAD_LCD_ENABLE { + MUX_PAD_LCD_ENABLE__LCDIF_ENABLE = ALT0, + MUX_PAD_LCD_ENABLE__LCDIF_RD_E = ALT1, + MUX_PAD_LCD_ENABLE__UART4_RX = ALT2, + MUX_PAD_LCD_ENABLE__SAI3_TX_SYNC = ALT3, + MUX_PAD_LCD_ENABLE__EIM_CS3_B = ALT4, + MUX_PAD_LCD_ENABLE__GPIO3_IO01 = ALT5, + MUX_PAD_LCD_ENABLE__ECSPI2_RDY = ALT8, + MUX_PAD_LCD_ENABLE__EPDC_SDLE = ALT9 +}; + +enum MUX_PAD_LCD_HSYNC { + MUX_PAD_LCD_HSYNC__LCDIF_HSYNC = ALT0, + MUX_PAD_LCD_HSYNC__LCDIF_RS = ALT1, + MUX_PAD_LCD_HSYNC__UART4_CTS_B = ALT2, + MUX_PAD_LCD_HSYNC__SAI3_TX_BCLK = ALT3, + MUX_PAD_LCD_HSYNC__WDOG3_WDOG_RST_B_DEB = ALT4, + MUX_PAD_LCD_HSYNC__GPIO3_IO02 = ALT5, + MUX_PAD_LCD_HSYNC__ECSPI2_SS1 = ALT8, + MUX_PAD_LCD_HSYNC__EPDC_SDOE = ALT9 +}; + +enum MUX_PAD_LCD_RESET { + MUX_PAD_LCD_RESET__LCDIF_RESET = ALT0, + MUX_PAD_LCD_RESET__LCDIF_CS = ALT1, + MUX_PAD_LCD_RESET__CA7_MX6ULL_EVENTI = ALT2, + MUX_PAD_LCD_RESET__SAI3_TX_DATA = ALT3, + MUX_PAD_LCD_RESET__WDOG1_WDOG_ANY = ALT4, + MUX_PAD_LCD_RESET__GPIO3_IO04 = ALT5, + MUX_PAD_LCD_RESET__ECSPI2_SS3 = ALT8, + MUX_PAD_LCD_RESET__EPDC_GDOE = ALT9 +}; + +enum MUX_PAD_LCD_VSYNC { + MUX_PAD_LCD_VSYNC__LCDIF_VSYNC = ALT0, + MUX_PAD_LCD_VSYNC__LCDIF_BUSY = ALT1, + MUX_PAD_LCD_VSYNC__UART4_RTS_B = ALT2, + MUX_PAD_LCD_VSYNC__SAI3_RX_DATA = ALT3, + MUX_PAD_LCD_VSYNC__WDOG2_WDOG_B = ALT4, + MUX_PAD_LCD_VSYNC__GPIO3_IO03 = ALT5, + MUX_PAD_LCD_VSYNC__ECSPI2_SS2 = ALT8, + MUX_PAD_LCD_VSYNC__EPDC_SDCE00 = ALT9 +}; + +enum MUX_PAD_NAND_ALE { + MUX_PAD_NAND_ALE__RAWNAND_ALE = ALT0, + MUX_PAD_NAND_ALE__USDHC2_RESET_B = ALT1, + MUX_PAD_NAND_ALE__QSPI_A_DQS = ALT2, + MUX_PAD_NAND_ALE__PWM3_OUT = ALT3, + MUX_PAD_NAND_ALE__EIM_ADDR17 = ALT4, + MUX_PAD_NAND_ALE__GPIO4_IO10 = ALT5, + MUX_PAD_NAND_ALE__ECSPI3_SS1 = ALT8 +}; + +enum MUX_PAD_NAND_CE0_B { + MUX_PAD_NAND_CE0_B__RAWNAND_CE0_B = ALT0, + MUX_PAD_NAND_CE0_B__USDHC1_DATA5 = ALT1, + MUX_PAD_NAND_CE0_B__QSPI_A_DATA01 = ALT2, + MUX_PAD_NAND_CE0_B__ECSPI3_SCLK = ALT3, + MUX_PAD_NAND_CE0_B__EIM_DTACK_B = ALT4, + MUX_PAD_NAND_CE0_B__GPIO4_IO13 = ALT5, + MUX_PAD_NAND_CE0_B__UART3_RX = ALT8 +}; + +enum MUX_PAD_NAND_CE1_B { + MUX_PAD_NAND_CE1_B__RAWNAND_CE1_B = ALT0, + MUX_PAD_NAND_CE1_B__USDHC1_DATA6 = ALT1, + MUX_PAD_NAND_CE1_B__QSPI_A_DATA02 = ALT2, + MUX_PAD_NAND_CE1_B__ECSPI3_MOSI = ALT3, + MUX_PAD_NAND_CE1_B__EIM_ADDR18 = ALT4, + MUX_PAD_NAND_CE1_B__GPIO4_IO14 = ALT5, + MUX_PAD_NAND_CE1_B__UART3_CTS_B = ALT8 +}; + +enum MUX_PAD_NAND_CLE { + MUX_PAD_NAND_CLE__RAWNAND_CLE = ALT0, + MUX_PAD_NAND_CLE__USDHC1_DATA7 = ALT1, + MUX_PAD_NAND_CLE__QSPI_A_DATA03 = ALT2, + MUX_PAD_NAND_CLE__ECSPI3_MISO = ALT3, + MUX_PAD_NAND_CLE__EIM_ADDR16 = ALT4, + MUX_PAD_NAND_CLE__GPIO4_IO15 = ALT5, + MUX_PAD_NAND_CLE__UART3_RTS_B = ALT8 +}; + +enum MUX_PAD_NAND_DATA00 { + MUX_PAD_NAND_DATA00__RAWNAND_DATA00 = ALT0, + MUX_PAD_NAND_DATA00__USDHC2_DATA0 = ALT1, + MUX_PAD_NAND_DATA00__QSPI_B_SS1_B = ALT2, + MUX_PAD_NAND_DATA00__KPP_ROW01 = ALT3, + MUX_PAD_NAND_DATA00__EIM_AD08 = ALT4, + MUX_PAD_NAND_DATA00__GPIO4_IO02 = ALT5, + MUX_PAD_NAND_DATA00__ECSPI4_RDY = ALT8 +}; + +enum MUX_PAD_NAND_DATA01 { + MUX_PAD_NAND_DATA01__RAWNAND_DATA01 = ALT0, + MUX_PAD_NAND_DATA01__USDHC2_DATA1 = ALT1, + MUX_PAD_NAND_DATA01__QSPI_B_DQS = ALT2, + MUX_PAD_NAND_DATA01__KPP_COL01 = ALT3, + MUX_PAD_NAND_DATA01__EIM_AD09 = ALT4, + MUX_PAD_NAND_DATA01__GPIO4_IO03 = ALT5, + MUX_PAD_NAND_DATA01__ECSPI4_SS1 = ALT8 +}; + +enum MUX_PAD_NAND_DATA02 { + MUX_PAD_NAND_DATA02__RAWNAND_DATA02 = ALT0, + MUX_PAD_NAND_DATA02__USDHC2_DATA2 = ALT1, + MUX_PAD_NAND_DATA02__QSPI_B_DATA00 = ALT2, + MUX_PAD_NAND_DATA02__KPP_ROW02 = ALT3, + MUX_PAD_NAND_DATA02__EIM_AD10 = ALT4, + MUX_PAD_NAND_DATA02__GPIO4_IO04 = ALT5, + MUX_PAD_NAND_DATA02__ECSPI4_SS2 = ALT8 +}; + +enum MUX_PAD_NAND_DATA03 { + MUX_PAD_NAND_DATA03__RAWNAND_DATA03 = ALT0, + MUX_PAD_NAND_DATA03__USDHC2_DATA3 = ALT1, + MUX_PAD_NAND_DATA03__QSPI_B_DATA01 = ALT2, + MUX_PAD_NAND_DATA03__KPP_COL02 = ALT3, + MUX_PAD_NAND_DATA03__EIM_AD11 = ALT4, + MUX_PAD_NAND_DATA03__GPIO4_IO05 = ALT5, + MUX_PAD_NAND_DATA03__ECSPI4_SS3 = ALT8 +}; + +enum MUX_PAD_NAND_DATA04 { + MUX_PAD_NAND_DATA04__RAWNAND_DATA04 = ALT0, + MUX_PAD_NAND_DATA04__USDHC2_DATA4 = ALT1, + MUX_PAD_NAND_DATA04__QSPI_B_DATA02 = ALT2, + MUX_PAD_NAND_DATA04__ECSPI4_SCLK = ALT3, + MUX_PAD_NAND_DATA04__EIM_AD12 = ALT4, + MUX_PAD_NAND_DATA04__GPIO4_IO06 = ALT5, + MUX_PAD_NAND_DATA04__UART2_TX = ALT8 +}; + +enum MUX_PAD_NAND_DATA05 { + MUX_PAD_NAND_DATA05__RAWNAND_DATA05 = ALT0, + MUX_PAD_NAND_DATA05__USDHC2_DATA5 = ALT1, + MUX_PAD_NAND_DATA05__QSPI_B_DATA03 = ALT2, + MUX_PAD_NAND_DATA05__ECSPI4_MOSI = ALT3, + MUX_PAD_NAND_DATA05__EIM_AD13 = ALT4, + MUX_PAD_NAND_DATA05__GPIO4_IO07 = ALT5, + MUX_PAD_NAND_DATA05__UART2_RX = ALT8 +}; + +enum MUX_PAD_NAND_DATA06 { + MUX_PAD_NAND_DATA06__RAWNAND_DATA06 = ALT0, + MUX_PAD_NAND_DATA06__USDHC2_DATA6 = ALT1, + MUX_PAD_NAND_DATA06__SAI2_RX_BCLK = ALT2, + MUX_PAD_NAND_DATA06__ECSPI4_MISO = ALT3, + MUX_PAD_NAND_DATA06__EIM_AD14 = ALT4, + MUX_PAD_NAND_DATA06__GPIO4_IO08 = ALT5, + MUX_PAD_NAND_DATA06__UART2_CTS_B = ALT8 +}; + +enum MUX_PAD_NAND_DATA07 { + MUX_PAD_NAND_DATA07__RAWNAND_DATA07 = ALT0, + MUX_PAD_NAND_DATA07__USDHC2_DATA7 = ALT1, + MUX_PAD_NAND_DATA07__QSPI_A_SS1_B = ALT2, + MUX_PAD_NAND_DATA07__ECSPI4_SS0 = ALT3, + MUX_PAD_NAND_DATA07__EIM_AD15 = ALT4, + MUX_PAD_NAND_DATA07__GPIO4_IO09 = ALT5, + MUX_PAD_NAND_DATA07__UART2_RTS_B = ALT8 +}; + +enum MUX_PAD_NAND_DQS { + MUX_PAD_NAND_DQS__RAWNAND_DQS = ALT0, + MUX_PAD_NAND_DQS__CSI_FIELD = ALT1, + MUX_PAD_NAND_DQS__QSPI_A_SS0_B = ALT2, + MUX_PAD_NAND_DQS__PWM5_OUT = ALT3, + MUX_PAD_NAND_DQS__EIM_WAIT = ALT4, + MUX_PAD_NAND_DQS__GPIO4_IO16 = ALT5, + MUX_PAD_NAND_DQS__SDMA_EXT_EVENT01 = ALT6, + MUX_PAD_NAND_DQS__SPDIF_EXT_CLK = ALT8 +}; + +enum MUX_PAD_NAND_READY_B { + MUX_PAD_NAND_READY_B__RAWNAND_READY_B = ALT0, + MUX_PAD_NAND_READY_B__USDHC1_DATA4 = ALT1, + MUX_PAD_NAND_READY_B__QSPI_A_DATA00 = ALT2, + MUX_PAD_NAND_READY_B__ECSPI3_SS0 = ALT3, + MUX_PAD_NAND_READY_B__EIM_CS1_B = ALT4, + MUX_PAD_NAND_READY_B__GPIO4_IO12 = ALT5, + MUX_PAD_NAND_READY_B__UART3_TX = ALT8 +}; + +enum MUX_PAD_NAND_RE_B { + MUX_PAD_NAND_RE_B__RAWNAND_RE_B = ALT0, + MUX_PAD_NAND_RE_B__USDHC2_CLK = ALT1, + MUX_PAD_NAND_RE_B__QSPI_B_SCLK = ALT2, + MUX_PAD_NAND_RE_B__KPP_ROW00 = ALT3, + MUX_PAD_NAND_RE_B__EIM_EB_B00 = ALT4, + MUX_PAD_NAND_RE_B__GPIO4_IO00 = ALT5, + MUX_PAD_NAND_RE_B__ECSPI3_SS2 = ALT8 +}; + +enum MUX_PAD_NAND_WE_B { + MUX_PAD_NAND_WE_B__RAWNAND_WE_B = ALT0, + MUX_PAD_NAND_WE_B__USDHC2_CMD = ALT1, + MUX_PAD_NAND_WE_B__QSPI_B_SS0_B = ALT2, + MUX_PAD_NAND_WE_B__KPP_COL00 = ALT3, + MUX_PAD_NAND_WE_B__EIM_EB_B01 = ALT4, + MUX_PAD_NAND_WE_B__GPIO4_IO01 = ALT5, + MUX_PAD_NAND_WE_B__ECSPI3_SS3 = ALT8 +}; + +enum MUX_PAD_NAND_WP_B { + MUX_PAD_NAND_WP_B__RAWNAND_WP_B = ALT0, + MUX_PAD_NAND_WP_B__USDHC1_RESET_B = ALT1, + MUX_PAD_NAND_WP_B__QSPI_A_SCLK = ALT2, + MUX_PAD_NAND_WP_B__PWM4_OUT = ALT3, + MUX_PAD_NAND_WP_B__EIM_BCLK = ALT4, + MUX_PAD_NAND_WP_B__GPIO4_IO11 = ALT5, + MUX_PAD_NAND_WP_B__ECSPI3_RDY = ALT8 +}; + +enum MUX_PAD_SD1_CLK { + MUX_PAD_SD1_CLK__USDHC1_CLK = ALT0, + MUX_PAD_SD1_CLK__GPT2_COMPARE2 = ALT1, + MUX_PAD_SD1_CLK__SAI2_MCLK = ALT2, + MUX_PAD_SD1_CLK__SPDIF_IN = ALT3, + MUX_PAD_SD1_CLK__EIM_ADDR20 = ALT4, + MUX_PAD_SD1_CLK__GPIO2_IO17 = ALT5, + MUX_PAD_SD1_CLK__USB_OTG1_OC = ALT8 +}; + +enum MUX_PAD_SD1_CMD { + MUX_PAD_SD1_CMD__USDHC1_CMD = ALT0, + MUX_PAD_SD1_CMD__GPT2_COMPARE1 = ALT1, + MUX_PAD_SD1_CMD__SAI2_RX_SYNC = ALT2, + MUX_PAD_SD1_CMD__SPDIF_OUT = ALT3, + MUX_PAD_SD1_CMD__EIM_ADDR19 = ALT4, + MUX_PAD_SD1_CMD__GPIO2_IO16 = ALT5, + MUX_PAD_SD1_CMD__SDMA_EXT_EVENT00 = ALT6, + MUX_PAD_SD1_CMD__USB_OTG1_PWR = ALT8 +}; + +enum MUX_PAD_SD1_DATA0 { + MUX_PAD_SD1_DATA0__USDHC1_DATA0 = ALT0, + MUX_PAD_SD1_DATA0__GPT2_COMPARE3 = ALT1, + MUX_PAD_SD1_DATA0__SAI2_TX_SYNC = ALT2, + MUX_PAD_SD1_DATA0__FLEXCAN1_TX = ALT3, + MUX_PAD_SD1_DATA0__EIM_ADDR21 = ALT4, + MUX_PAD_SD1_DATA0__GPIO2_IO18 = ALT5, + MUX_PAD_SD1_DATA0__ANATOP_OTG1_ID = ALT8 +}; + +enum MUX_PAD_SD1_DATA1 { + MUX_PAD_SD1_DATA1__USDHC1_DATA1 = ALT0, + MUX_PAD_SD1_DATA1__GPT2_CLK = ALT1, + MUX_PAD_SD1_DATA1__SAI2_TX_BCLK = ALT2, + MUX_PAD_SD1_DATA1__FLEXCAN1_RX = ALT3, + MUX_PAD_SD1_DATA1__EIM_ADDR22 = ALT4, + MUX_PAD_SD1_DATA1__GPIO2_IO19 = ALT5, + MUX_PAD_SD1_DATA1__USB_OTG2_PWR = ALT8 +}; + +enum MUX_PAD_SD1_DATA2 { + MUX_PAD_SD1_DATA2__USDHC1_DATA2 = ALT0, + MUX_PAD_SD1_DATA2__GPT2_CAPTURE1 = ALT1, + MUX_PAD_SD1_DATA2__SAI2_RX_DATA = ALT2, + MUX_PAD_SD1_DATA2__FLEXCAN2_TX = ALT3, + MUX_PAD_SD1_DATA2__EIM_ADDR23 = ALT4, + MUX_PAD_SD1_DATA2__GPIO2_IO20 = ALT5, + MUX_PAD_SD1_DATA2__CCM_CLKO1 = ALT6, + MUX_PAD_SD1_DATA2__USB_OTG2_OC = ALT8 +}; + +enum MUX_PAD_SD1_DATA3 { + MUX_PAD_SD1_DATA3__USDHC1_DATA3 = ALT0, + MUX_PAD_SD1_DATA3__GPT2_CAPTURE2 = ALT1, + MUX_PAD_SD1_DATA3__SAI2_TX_DATA = ALT2, + MUX_PAD_SD1_DATA3__FLEXCAN2_RX = ALT3, + MUX_PAD_SD1_DATA3__EIM_ADDR24 = ALT4, + MUX_PAD_SD1_DATA3__GPIO2_IO21 = ALT5, + MUX_PAD_SD1_DATA3__CCM_CLKO2 = ALT6, + MUX_PAD_SD1_DATA3__ANATOP_OTG2_ID = ALT8 +}; + +enum MUX_PAD_UART1_CTS_B { + MUX_PAD_UART1_CTS_B__UART1_CTS_B = ALT0, + MUX_PAD_UART1_CTS_B__ENET1_RX_CLK = ALT1, + MUX_PAD_UART1_CTS_B__USDHC1_WP = ALT2, + MUX_PAD_UART1_CTS_B__CSI_DATA04 = ALT3, + MUX_PAD_UART1_CTS_B__ENET2_1588_EVENT1_IN = ALT4, + MUX_PAD_UART1_CTS_B__GPIO1_IO18 = ALT5, + MUX_PAD_UART1_CTS_B__USDHC2_WP = ALT8, + MUX_PAD_UART1_CTS_B__UART5_CTS_B = ALT9 +}; + +enum MUX_PAD_UART1_RTS_B { + MUX_PAD_UART1_RTS_B__UART1_RTS_B = ALT0, + MUX_PAD_UART1_RTS_B__ENET1_TX_ER = ALT1, + MUX_PAD_UART1_RTS_B__USDHC1_CD_B = ALT2, + MUX_PAD_UART1_RTS_B__CSI_DATA05 = ALT3, + MUX_PAD_UART1_RTS_B__ENET2_1588_EVENT1_OUT = ALT4, + MUX_PAD_UART1_RTS_B__GPIO1_IO19 = ALT5, + MUX_PAD_UART1_RTS_B__USDHC2_CD_B = ALT8, + MUX_PAD_UART1_RTS_B__UART5_RTS_B = ALT9 +}; + +enum MUX_PAD_UART1_RX_DATA { + MUX_PAD_UART1_RX_DATA__UART1_RX = ALT0, + MUX_PAD_UART1_RX_DATA__ENET1_RDATA03 = ALT1, + MUX_PAD_UART1_RX_DATA__I2C3_SDA = ALT2, + MUX_PAD_UART1_RX_DATA__CSI_DATA03 = ALT3, + MUX_PAD_UART1_RX_DATA__GPT1_CLK = ALT4, + MUX_PAD_UART1_RX_DATA__GPIO1_IO17 = ALT5, + MUX_PAD_UART1_RX_DATA__SPDIF_IN = ALT8, + MUX_PAD_UART1_RX_DATA__UART5_RX = ALT9 +}; + +enum MUX_PAD_UART1_TX_DATA { + MUX_PAD_UART1_TX_DATA__UART1_TX = ALT0, + MUX_PAD_UART1_TX_DATA__ENET1_RDATA02 = ALT1, + MUX_PAD_UART1_TX_DATA__I2C3_SCL = ALT2, + MUX_PAD_UART1_TX_DATA__CSI_DATA02 = ALT3, + MUX_PAD_UART1_TX_DATA__GPT1_COMPARE1 = ALT4, + MUX_PAD_UART1_TX_DATA__GPIO1_IO16 = ALT5, + MUX_PAD_UART1_TX_DATA__SPDIF_OUT = ALT8, + MUX_PAD_UART1_TX_DATA__UART5_TX = ALT9 +}; + +enum MUX_PAD_UART2_CTS_B { + MUX_PAD_UART2_CTS_B__UART2_CTS_B = ALT0, + MUX_PAD_UART2_CTS_B__ENET1_CRS = ALT1, + MUX_PAD_UART2_CTS_B__FLEXCAN2_TX = ALT2, + MUX_PAD_UART2_CTS_B__CSI_DATA08 = ALT3, + MUX_PAD_UART2_CTS_B__GPT1_COMPARE2 = ALT4, + MUX_PAD_UART2_CTS_B__GPIO1_IO22 = ALT5, + MUX_PAD_UART2_CTS_B__SJC_DE_B = ALT7, + MUX_PAD_UART2_CTS_B__ECSPI3_MOSI = ALT8 +}; + +enum MUX_PAD_UART2_RTS_B { + MUX_PAD_UART2_RTS_B__UART2_RTS_B = ALT0, + MUX_PAD_UART2_RTS_B__ENET1_COL = ALT1, + MUX_PAD_UART2_RTS_B__FLEXCAN2_RX = ALT2, + MUX_PAD_UART2_RTS_B__CSI_DATA09 = ALT3, + MUX_PAD_UART2_RTS_B__GPT1_COMPARE3 = ALT4, + MUX_PAD_UART2_RTS_B__GPIO1_IO23 = ALT5, + MUX_PAD_UART2_RTS_B__SJC_FAIL = ALT7, + MUX_PAD_UART2_RTS_B__ECSPI3_MISO = ALT8 +}; + +enum MUX_PAD_UART2_RX_DATA { + MUX_PAD_UART2_RX_DATA__UART2_RX = ALT0, + MUX_PAD_UART2_RX_DATA__ENET1_TDATA03 = ALT1, + MUX_PAD_UART2_RX_DATA__I2C4_SDA = ALT2, + MUX_PAD_UART2_RX_DATA__CSI_DATA07 = ALT3, + MUX_PAD_UART2_RX_DATA__GPT1_CAPTURE2 = ALT4, + MUX_PAD_UART2_RX_DATA__GPIO1_IO21 = ALT5, + MUX_PAD_UART2_RX_DATA__SJC_DONE = ALT7, + MUX_PAD_UART2_RX_DATA__ECSPI3_SCLK = ALT8 +}; + +enum MUX_PAD_UART2_TX_DATA { + MUX_PAD_UART2_TX_DATA__UART2_TX = ALT0, + MUX_PAD_UART2_TX_DATA__ENET1_TDATA02 = ALT1, + MUX_PAD_UART2_TX_DATA__I2C4_SCL = ALT2, + MUX_PAD_UART2_TX_DATA__CSI_DATA06 = ALT3, + MUX_PAD_UART2_TX_DATA__GPT1_CAPTURE1 = ALT4, + MUX_PAD_UART2_TX_DATA__GPIO1_IO20 = ALT5, + MUX_PAD_UART2_TX_DATA__ECSPI3_SS0 = ALT8 +}; + +enum MUX_PAD_UART3_CTS_B { + MUX_PAD_UART3_CTS_B__UART3_CTS_B = ALT0, + MUX_PAD_UART3_CTS_B__ENET2_RX_CLK = ALT1, + MUX_PAD_UART3_CTS_B__FLEXCAN1_TX = ALT2, + MUX_PAD_UART3_CTS_B__CSI_DATA10 = ALT3, + MUX_PAD_UART3_CTS_B__ENET1_1588_EVENT1_IN = ALT4, + MUX_PAD_UART3_CTS_B__GPIO1_IO26 = ALT5, + MUX_PAD_UART3_CTS_B__EPIT2_OUT = ALT8 +}; + +enum MUX_PAD_UART3_RTS_B { + MUX_PAD_UART3_RTS_B__UART3_RTS_B = ALT0, + MUX_PAD_UART3_RTS_B__ENET2_TX_ER = ALT1, + MUX_PAD_UART3_RTS_B__FLEXCAN1_RX = ALT2, + MUX_PAD_UART3_RTS_B__CSI_DATA11 = ALT3, + MUX_PAD_UART3_RTS_B__ENET1_1588_EVENT1_OUT = ALT4, + MUX_PAD_UART3_RTS_B__GPIO1_IO27 = ALT5, + MUX_PAD_UART3_RTS_B__WDOG1_WDOG_B = ALT8 +}; + +enum MUX_PAD_UART3_RX_DATA { + MUX_PAD_UART3_RX_DATA__UART3_RX = ALT0, + MUX_PAD_UART3_RX_DATA__ENET2_RDATA03 = ALT1, + MUX_PAD_UART3_RX_DATA__RESERVED_ALT2 = ALT2, + MUX_PAD_UART3_RX_DATA__CSI_DATA00 = ALT3, + MUX_PAD_UART3_RX_DATA__UART2_RTS_B = ALT4, + MUX_PAD_UART3_RX_DATA__GPIO1_IO25 = ALT5, + MUX_PAD_UART3_RX_DATA__EPIT1_OUT = ALT8 +}; + +enum MUX_PAD_UART3_TX_DATA { + MUX_PAD_UART3_TX_DATA__UART3_TX = ALT0, + MUX_PAD_UART3_TX_DATA__ENET2_RDATA02 = ALT1, + MUX_PAD_UART3_TX_DATA__RESERVED_ALT2 = ALT2, + MUX_PAD_UART3_TX_DATA__CSI_DATA01 = ALT3, + MUX_PAD_UART3_TX_DATA__UART2_CTS_B = ALT4, + MUX_PAD_UART3_TX_DATA__GPIO1_IO24 = ALT5, + MUX_PAD_UART3_TX_DATA__SJC_JTAG_ACT = ALT7, + MUX_PAD_UART3_TX_DATA__ANATOP_OTG1_ID = ALT8 +}; + +enum MUX_PAD_UART4_RX_DATA { + MUX_PAD_UART4_RX_DATA__UART4_RX = ALT0, + MUX_PAD_UART4_RX_DATA__ENET2_TDATA03 = ALT1, + MUX_PAD_UART4_RX_DATA__I2C1_SDA = ALT2, + MUX_PAD_UART4_RX_DATA__CSI_DATA13 = ALT3, + MUX_PAD_UART4_RX_DATA__CSU_CSU_ALARM_AUT01 = ALT4, + MUX_PAD_UART4_RX_DATA__GPIO1_IO29 = ALT5, + MUX_PAD_UART4_RX_DATA__ECSPI2_SS0 = ALT8, + MUX_PAD_UART4_RX_DATA__EPDC_PWRCTRL01 = ALT9 +}; + +enum MUX_PAD_UART4_TX_DATA { + MUX_PAD_UART4_TX_DATA__UART4_TX = ALT0, + MUX_PAD_UART4_TX_DATA__ENET2_TDATA02 = ALT1, + MUX_PAD_UART4_TX_DATA__I2C1_SCL = ALT2, + MUX_PAD_UART4_TX_DATA__CSI_DATA12 = ALT3, + MUX_PAD_UART4_TX_DATA__CSU_CSU_ALARM_AUT02 = ALT4, + MUX_PAD_UART4_TX_DATA__GPIO1_IO28 = ALT5, + MUX_PAD_UART4_TX_DATA__ECSPI2_SCLK = ALT8 +}; + +enum MUX_PAD_UART5_RX_DATA { + MUX_PAD_UART5_RX_DATA__UART5_RX = ALT0, + MUX_PAD_UART5_RX_DATA__ENET2_COL = ALT1, + MUX_PAD_UART5_RX_DATA__I2C2_SDA = ALT2, + MUX_PAD_UART5_RX_DATA__CSI_DATA15 = ALT3, + MUX_PAD_UART5_RX_DATA__CSU_CSU_INT_DEB = ALT4, + MUX_PAD_UART5_RX_DATA__GPIO1_IO31 = ALT5, + MUX_PAD_UART5_RX_DATA__ECSPI2_MISO = ALT8, + MUX_PAD_UART5_RX_DATA__EPDC_PWRCTRL03 = ALT9 +}; + +enum MUX_PAD_UART5_TX_DATA { + MUX_PAD_UART5_TX_DATA__GPIO1_IO30 = ALT5, + MUX_PAD_UART5_TX_DATA__ECSPI2_MOSI = ALT8, + MUX_PAD_UART5_TX_DATA__EPDC_PWRCTRL02 = ALT9, + MUX_PAD_UART5_TX_DATA__UART5_TX = ALT0, + MUX_PAD_UART5_TX_DATA__ENET2_CRS = ALT1, + MUX_PAD_UART5_TX_DATA__I2C2_SCL = ALT2, + MUX_PAD_UART5_TX_DATA__CSI_DATA14 = ALT3, + MUX_PAD_UART5_TX_DATA__CSU_CSU_ALARM_AUT00 = ALT4 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* IOMUX_MUX_ENUMS_H */ +/* clang-format on */ diff --git a/adc/ade9113/log.h b/adc/ade9113/log.h new file mode 100644 index 000000000..ac0af1c9f --- /dev/null +++ b/adc/ade9113/log.h @@ -0,0 +1,29 @@ +/* + * Phoenix-RTOS + * + * Minimal log macros + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#ifndef ADE9113_LOG_H +#define ADE9113_LOG_H + +#include + +#ifndef LOG_TAG +#error LOG_TAG is not defined +#endif + +#define log_error(fmt, ...) \ + do { \ + fprintf(stdout, LOG_TAG fmt "\n", ##__VA_ARGS__); \ + } while (0) + +#define log_info(fmt, ...) log_error(fmt, ##__VA_ARGS__); + +#endif /* end of include guard: ADE9113_LOG_H */ diff --git a/adc/ade9113/main.c b/adc/ade9113/main.c new file mode 100644 index 000000000..6f462b495 --- /dev/null +++ b/adc/ade9113/main.c @@ -0,0 +1,635 @@ +/* + * Phoenix-RTOS + * + * ADC driver continuously reading samples from 4 chained ADE9113 + * + * This code was written for specific HW configuration. This is included here to serve as an example / base that can + * be adapted to work with other platforms. + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define LOG_TAG "ADE9113-DRV: " + +#include "log.h" +#include "dma.h" +#include "sample.h" +#include "msgapi.h" +#include "imx6ull.h" +#include "ade9113.h" +#include "ade9113_regs.h" +#include "iomux_mux_enums.h" + + +#define SAMPLE_SIZE 32 + + +/* value written to SCRATCH registers to verify reads (arbitrary unique value for each ADC chip) */ +static const uint8_t scratchValues[4] = { 0xa0, 0xa1, 0xa2, 0xa3 }; + + +struct adcCtx { + volatile struct imx6ull_regs_ecspi *spi; + + volatile unsigned rxCount; + volatile unsigned crcErrors; + volatile unsigned valueErrors; + + struct sampleCtx samples; + struct msgapiCtx msgapi; +}; + + +static struct adcCtx common_adc; + + +/* Basic PWM3 configuration that generates 16.5 MHz (IPG_CLK / 4) */ +static int initPwm(void) +{ + volatile struct imx6ull_regs_pwm *pwm = mmap(NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, IMX_PWM3_BASE); + if (pwm == MAP_FAILED) { + printf("Failed to map: %p\n", pwm); + return -1; + } + + /* enable pwm clock */ + platformctl(&(platformctl_t) { + .action = pctl_set, + .type = pctl_devclock, + .devclock.dev = pctl_clk_pwm3, + .devclock.state = 3 }); + + pwm->PWMCR = 0; /* disable PWM */ + + /* 50% duty cycle - output frequency is half of the prescaller output */ + pwm->PWMPR = 0; + pwm->PWMSAR = 1; + + const uint32_t clockSource_ipgClkHighfreq = 2; + + uint32_t clockSource = clockSource_ipgClkHighfreq; + uint32_t prescaller = 1; /* divide IPG by 2 */ + uint32_t enable = 1; + + pwm->PWMCR = (0 | ((clockSource & 0x03) << 16) | ((prescaller & 0xfff) << 4) | ((enable & 0x01) << 0x00)); + + munmap((void *)pwm, _PAGE_SIZE); + return 0; +} + + +static void configureEcspi(volatile struct imx6ull_regs_ecspi *spi) +{ + /* defaults from ecspi_init */ + spi->CONREG = 0x007000F1; + spi->CONFIGREG = 0x00000F00; + + const uint16_t burst = 16 * 4 * 8; + spi->CONREG = (spi->CONREG & ~(0xFFF << 20)) | ((burst - 1) << 20); + + spi->CONFIGREG &= ~(0x01 << 12); /* set SS_POL[0] to invert SS on channel 0 (buffered with schmitt-trigger inverter) */ + spi->CONFIGREG &= ~(0x01 << 4); /* set SCLK_POL[0] */ + spi->CONFIGREG |= (0x01 << 0); /* set SCLK_PHA[0] */ + + /* + * spi_freq = 66 Mhz / (pre + 1) * (post + 1) + * for 8k sample rate we need SPI clock >= 5 Mhz (8000 * 64 * 8 + margin) + * + * pre=4 post=0 -> ~13.2 Mhz (current value) + * pre=12 post=0 -> ~5.0 Mhz (required minimum) + * + * For now using higher than needed to see if it is stable enough. Can be lowered later to improve robustness + */ + /* TODO: consider changing this to minimum once this is validated on semi-final HW in different conditions */ + uint8_t divPre = 4; + uint8_t divPost = 0; + ecspi_setClockDiv(ecspi1, divPre, divPost); +} + + +static void resetEcspi(volatile struct imx6ull_regs_ecspi *spi) +{ + /* reset ecspi1 */ + usleep(1000); + spi->CONREG = 0; + usleep(1000); + + configureEcspi(spi); +} + + +bool dmaRxCb(const uint8_t *data, size_t size) +{ + struct adcCtx *ctx = &common_adc; + sample_writeStart(&ctx->samples); + for (int sample = 0; sample < size / 64; ++sample) { + sample_write(&ctx->samples, (const uint8_t *)"\x00\x00\x00\x00\x00", 5); /* header (aligns full sample to 32 bytes) */ + for (int chip = 0; chip < 4; ++chip) { + /* response from first chip in chain is received last */ + size_t offs = (sample * 64) + (3 - chip) * 16; + uint8_t rsp[16]; + for (int i = 0; i + 4 <= sizeof(rsp); i += 4) { + uint8_t *dst = rsp + i; + const uint8_t *src = data + offs + i; + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + } + + const char *error = ade9113_checkResponse(rsp, sizeof(rsp)); + if (error != NULL) { + ctx->crcErrors += 1; + memset(rsp, 0, sizeof(rsp)); + } + else if (rsp[13] != scratchValues[chip]) { + ctx->valueErrors += 1; + memset(rsp, 0, sizeof(rsp)); + } + else { + } + sample_write(&ctx->samples, &rsp[1], 3); // I LO,MID,HI + sample_write(&ctx->samples, &rsp[5], 3); // V1 LO,MID,HI + if (chip == 3) { + // TODO: make configurable + sample_write(&ctx->samples, &rsp[9], 3); // V2 LO,MID,HI + } + } + } + ctx->rxCount += size; + sample_writeEnd(&ctx->samples); + return true; /* true - keep reading samples */ +} + + +static int spiExchangeCb(void *userData, const uint8_t *dataIn, uint8_t *dataOut, size_t len) +{ + return (ecspi_exchangeBusy(ecspi1, dataIn, dataOut, len) == 0) ? 0 : -1; +} + + +static void msgThread(void *voidCtx) +{ + struct adcCtx *ctx = (struct adcCtx *)voidCtx; + msgapi_serve(&ctx->msgapi); + endthread(); +} + +volatile sig_atomic_t pendingTerm = 0; + + +static void signalHandler(int signum) +{ + pendingTerm = 1; +} + + +struct args { + unsigned verbosity; + bool debug; + enum ade9113_config0_stream_dbg dbgMode; + enum ade9113_config_filt_datapath_config datapath; + unsigned bufferSize; /* sample buffer size expressed as power of 2 */ + const char *configCrc; +}; + + +struct strEnumDef { + const char *name; + unsigned value; +}; + +const struct strEnumDef modeEnumDefs[] = { + { "normal", (unsigned)ADE9113_CONFIG0_STREAM_DBG__NORMAL_MODE }, + { "static", (unsigned)ADE9113_CONFIG0_STREAM_DBG__STATIC_MODE }, + { "count", (unsigned)ADE9113_CONFIG0_STREAM_DBG__COUNT_MODE }, + { NULL, 0 } +}; + + +static int parseStrEnum(char opt, const char *value, const struct strEnumDef *defs) +{ + for (const struct strEnumDef *def = defs; def->name != NULL; ++def) { + if (strcmp(def->name, value) == 0) { + return def->value; + } + } + log_error("value for option `-%c` is not recognised. Allowed options:", opt); + for (const struct strEnumDef *def = defs; def->name != NULL; ++def) { + fprintf(stderr, "* \"%s\"", def->name); + } + return -1; +} + + +static int parseIntEnum(char opt, const char *value, unsigned min, unsigned max) +{ + char *end = NULL; + int result = strtoul(value, &end, 10); + if (end == NULL || *end != '\0') { + log_error("failed to parse int value for option `-%c`", opt); + return -1; + } + if ((result < min) || (result > max) || (result > INT_MAX)) { + log_error("value for option `-%c` is out of allowed range <%u;%u>", opt, min, max); + return -1; + } + return result; +} + + +static void printUsage(const char *progname) +{ + printf("Usage: %s [OPTIONS]\n", (progname != NULL) ? progname : "prog"); + puts("\t-h This help message"); + puts("\t-v Increase verbosity"); + puts("\t-d Debug mode"); + puts("\t-b [12-28] Sample buffer size (power of 2)"); + puts("\t-m {normal,static,count} Sample stream mode"); + puts("\t-s [0-7] ADE9113 datapath. Default is 4 (8 kHz + LPF + comp)"); + puts("\t-c CRC values used to verify ADC config: `0123,4567,89ab,cdef`. Defaults to no CRC check"); +} + + +static int parseArgs(struct args *parsed, int argc, char **argv) +{ + *parsed = (struct args) { + .verbosity = 0, + .bufferSize = 18, + .debug = false, + .dbgMode = ADE9113_CONFIG0_STREAM_DBG__NORMAL_MODE, + .datapath = ADE9113_CONFIG_FILT_DATAPATH_CONFIG__COMP_LPF_8_KHZ, + .configCrc = NULL + }; + while (true) { + int c = getopt(argc, argv, "hdvm:s:b:c:"); + if (c == -1) { + break; + } + switch (c) { + case 'm': { + int value = parseStrEnum(c, optarg, modeEnumDefs); + if (value < 0) { + log_error("invalid stream mode (option `-m`)"); + return -1; + } + parsed->dbgMode = value; + break; + } + case 's': { + int value = parseIntEnum(c, optarg, 0, 7); + if (value < 0) { + return -1; + } + parsed->datapath = value; + break; + } + case 'b': { + int value = parseIntEnum(c, optarg, 12, 28); + if (value < 0) { + return -1; + } + parsed->bufferSize = value; + break; + } + case 'c': { + parsed->configCrc = optarg; + break; + } + case 'h': + default: { + printUsage(argv[0]); + return -1; + } + } + } + return 0; +} + + +int gpioWrite(const char *path, uint32_t value, uint32_t mask) +{ + int fd = open(path, O_RDWR); + if (fd < 0) { + return -1; + } + const uint32_t data[2] = { value, mask }; /* no endianness - same as in driver */ + int ret = write(fd, (const uint8_t *)data, 8); + close(fd); + return (ret > 0) ? 0 : -1; +} + + +int adcInit(struct adcCtx *ctx, addr_t spiBase, unsigned bufferSize) +{ + ctx->spi = mmap(NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, spiBase); + if (ctx->spi == MAP_FAILED) { + log_error("failed to allocate spi1 buffer"); + return -1; + } + + return sample_init(&ctx->samples, bufferSize, SAMPLE_SIZE); +} + + +int adcConfigure(struct adcCtx *ctx, const struct args *args) +{ + resetEcspi(ctx->spi); + + struct ade9113_ctx ade = { spiExchangeCb, NULL }; + + ade9113_writeRegs(&ade, ADE9113_WR_LOCK, ADE9113_WR_LOCK__UNLOCK_KEY); + ade9113_writeRegs(&ade, ADE9113_SWRST, ADE9113_SWRST__SOFTWARE_RESET_COMMAND); + + usleep(300 * 1000); + + /* enable clock passthrough from first chip */ + ade9113_writeRegsDifferent( + &ade, + ADE9113_CONFIG0, + ADE9113_CONFIG0_ENC(.crc_en_spi_write = 1, .clkout_en = 1), + ADE9113_CONFIG0_ENC(.crc_en_spi_write = 1, .clkout_en = 0), + ADE9113_CONFIG0_ENC(.crc_en_spi_write = 1, .clkout_en = 0), + ADE9113_CONFIG0_ENC(.crc_en_spi_write = 1, .clkout_en = 0)); + + /* now that remaining chips were provided with clock we can reset them */ + ade9113_writeRegsDifferent( + &ade, + ADE9113_SWRST, + 0, + ADE9113_SWRST__SOFTWARE_RESET_COMMAND, + ADE9113_SWRST__SOFTWARE_RESET_COMMAND, + ADE9113_SWRST__SOFTWARE_RESET_COMMAND); + usleep(300 * 1000); + + /* prevent interrupts from driving IRQ pin */ + ade9113_writeRegs(&ade, ADE9113_MASK0, 0); + ade9113_writeRegs(&ade, ADE9113_MASK1, 0); + ade9113_writeRegs(&ade, ADE9113_MASK2, 0); + + /* synchronize sampling between chips (two separate writes to different bits as described in datasheet) */ + ade9113_writeRegs(&ade, ADE9113_SYNC_SNAP, ADE9113_SYNC_SNAP_ENC(.align = 1)); + ade9113_writeRegs(&ade, ADE9113_SYNC_SNAP, ADE9113_SYNC_SNAP_ENC(.snapshot = 1)); + + if (args->dbgMode != ADE9113_CONFIG0_STREAM_DBG__NORMAL_MODE) { + /* clear all counters */ + for (uint8_t reg = ADE9113_I_WAV_HI; reg <= ADE9113_V2_WAV_LO; ++reg) { + ade9113_writeRegs(&ade, reg, 0); + } + } + + ade9113_writeRegs(&ade, ADE9113_CONFIG_FILT, ADE9113_CONFIG_FILT_ENC(.datapath_config = args->datapath)); + + ade9113_writeRegsDifferent( + &ade, + ADE9113_CONFIG0, + ADE9113_CONFIG0_ENC(.stream_dbg = args->dbgMode, .crc_en_spi_write = 1, .clkout_en = 1), + ADE9113_CONFIG0_ENC(.stream_dbg = args->dbgMode, .crc_en_spi_write = 1, .clkout_en = 0), + ADE9113_CONFIG0_ENC(.stream_dbg = args->dbgMode, .crc_en_spi_write = 1, .clkout_en = 0), + ADE9113_CONFIG0_ENC(.stream_dbg = args->dbgMode, .crc_en_spi_write = 1, .clkout_en = 0)); + + /* scratch values will be checked in DMA responses to ensure there is no shift */ + ade9113_writeRegsDifferent(&ade, ADE9113_SCRATCH, scratchValues[0], scratchValues[1], scratchValues[2], scratchValues[3]); + + ade9113_writeRegs(&ade, ADE9113_WR_LOCK, ADE9113_WR_LOCK__LOCK_KEY); + ade9113_writeRegs(&ade, ADE9113_CONFIG_CRC, ADE9113_CONFIG_CRC_ENC(.crc_force = 1)); + + for (int try = 0; try < 10; ++try) { + uint8_t values[4]; + ade9113_readRegs(&ade, ADE9113_CONFIG_CRC, values, sizeof(values)); + bool ready = true; + for (int i = 0; i < 4; ++i) { + if ((values[i] & 0x01) == 0) { + ready = false; + break; + } + } + if (ready) { + break; + } + usleep(50000); + } + + { + uint8_t crcHigh[4]; + ade9113_readRegs(&ade, ADE9113_CRC_RESULT_HI, crcHigh, sizeof(crcHigh)); + + uint8_t crcLow[4]; + ade9113_readRegs(&ade, ADE9113_CRC_RESULT_LO, crcLow, sizeof(crcLow)); + + uint16_t crc[4]; + for (int i = 0; i < 4; ++i) { + crc[i] = (crcHigh[i] << 8) | crcLow[i]; + } + + char strCrc[5 * 4]; + sprintf(strCrc, "%04x,%04x,%04x,%04x", crc[0], crc[1], crc[2], crc[3]); + assert(strCrc[sizeof(strCrc) - 1] == '\0'); + + log_info("config CRC: %s", strCrc); + if ((args->configCrc != NULL) && (strcmp(args->configCrc, strCrc) != 0)) { + log_info("expected CRC: %s", args->configCrc); + log_error("ERROR: CRC mismatch"); + return -1; + }; + } + return 0; +} + + +int main(int argc, char **argv) +{ + struct args args; + if (parseArgs(&args, argc, argv) < 0) { + return EX_USAGE; + } + + struct sigaction sa; + sa.sa_handler = signalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; /* Restart functions if interrupted by handler */ + + if (sigaction(SIGINT, &sa, NULL) == -1) { + log_error("sigaction failed"); + return -1; + } + + if (sigaction(SIGTERM, &sa, NULL) == -1) { + log_error("sigaction failed"); + return -1; + } + + struct muxConfig { + uint16_t mux; + uint8_t mode; + }; + const struct muxConfig muxConfigs[] = { + /* PWM */ + { .mux = pctl_mux_lcd_d2, .mode = MUX_PAD_LCD_DATA02__PWM3_OUT }, + + /* GPIO */ + { .mux = pctl_mux_tamper2, .mode = MUX_PAD_SNVS_TAMPER2__GPIO5_IO02 }, /* ADC_RESET (connected to RESET2) */ + { .mux = pctl_mux_lcd_rst, .mode = MUX_PAD_LCD_RESET__GPIO3_IO04 }, /* ADC_RESET2 */ + { .mux = pctl_mux_lcd_hsync, .mode = MUX_PAD_LCD_HSYNC__GPIO3_IO02 }, /* ADC_IRQN */ + { .mux = pctl_mux_lcd_vsync, .mode = MUX_PAD_LCD_VSYNC__GPIO3_IO03 }, /* ADC_DREADY_L2 */ + { .mux = pctl_mux_lcd_en, .mode = MUX_PAD_LCD_ENABLE__GPIO3_IO01 }, /* ADC_IRQ1 */ + { .mux = pctl_mux_lcd_clk, .mode = MUX_PAD_LCD_CLK__GPIO3_IO00 }, /* ADC_IRQ2 */ + { .mux = pctl_mux_lcd_d0, .mode = MUX_PAD_LCD_DATA00__GPIO3_IO05 }, /* ADC_IRQ3 */ + { .mux = pctl_mux_lcd_d12, .mode = MUX_PAD_LCD_DATA12__ECSPI1_RDY }, /* ADC_DREADY */ + }; + for (int i = 0; i < sizeof(muxConfigs) / sizeof(*muxConfigs); ++i) { + const struct muxConfig *cfg = &muxConfigs[i]; + platformctl(&(platformctl_t) { .action = pctl_set, .type = pctl_iomux, .iomux = { .mux = cfg->mux, .sion = 0, .mode = cfg->mode } }); + } + + if (initPwm()) { + log_error("failed to initialize PWM"); + return EX_OSERR; + } + + /* set RESET pin to HIGH (push-pull output) */ + const int resetPin = 2; + gpioWrite("/dev/gpio5/dir", 1 << resetPin, 1 << resetPin); + gpioWrite("/dev/gpio5/port", 1 << resetPin, 1 << resetPin); + + /* Initialize ecspi1 with channel 0 */ + if (ecspi_init(ecspi1, (1 << 0)) < 0) { + log_error("failed to initialize ecspi"); + return EX_OSERR; + } + + /* pull up reset2 pin */ + platformctl(&(platformctl_t) { + .action = pctl_set, + .type = pctl_iopad, + .iopad = { .pad = pctl_pad_tamper2, .pus = 0b11, .pke = 0, .speed = 2 } }); + + /* SPI pads - fast, strong and with hysteresis */ + uint8_t spiPads[] = { pctl_mux_lcd_d20, pctl_mux_lcd_d21, pctl_mux_lcd_d22, pctl_mux_lcd_d23 }; + for (int i = 0; i < sizeof(spiPads) / sizeof(*spiPads); ++i) { + platformctl(&(platformctl_t) { + .action = pctl_set, + .type = pctl_iopad, + .iopad = { + .pad = spiPads[i], + .hys = 1, /* enable hysteresis */ + .pus = 0b00, /* 100kOhm pull down */ + .pue = 1, /* pull on power down */ + .pke = 1, + .ode = 0, /* open drain disabled */ + .speed = 2, + .dse = 0b111, /* strong drive */ + .sre = 1, /* fast slew rate */ + } }); + } + + /* IRQN pad - enable internal pullup */ + platformctl(&(platformctl_t) { + .action = pctl_set, + .type = pctl_iopad, + .iopad = { + .pad = pctl_pad_lcd_hsync, + .hys = 1, /* enable hysteresis */ + .pus = 0b11, /* 22kOhm pull up */ + .pue = 1, /* pull on power down */ + .pke = 1, + .ode = 0, /* open drain disabled */ + .speed = 2, + .dse = 0b111, /* strong drive */ + .sre = 1, /* fast slew rate */ + } }); + + struct adcCtx *ctx = &common_adc; + + if (adcInit(ctx, IMX_ECSPI1_BASE, args.bufferSize) < 0) { + return EX_OSERR; + }; + adcConfigure(ctx, &args); + + if (dma_init(_PAGE_SIZE * 2, 2, dmaRxCb) < 0) { + log_error("failed to init DMA"); + return -1; + } + + msgapi_init(&ctx->msgapi, &ctx->samples, "ade9113"); + + static uint8_t msgStacks[2][_PAGE_SIZE] __attribute__((aligned(8))); + beginthread(msgThread, 2, msgStacks[0], _PAGE_SIZE, ctx); + beginthread(msgThread, 2, msgStacks[1], _PAGE_SIZE, ctx); + + resetEcspi(ctx->spi); + + /* + * Configure eCSPI for DMA usage: + * - TX burst will be triggered from DREADY pin + * - DMA will write to TX FIFO when it reaches 0 words (empty) + * - DMA will read from RX FIFO when it reaches 16 words (full 64 byte response from all chips) + */ + ctx->spi->CONREG = (ctx->spi->CONREG & ~(0b11 << 16)) | (0b01 << 16); /* TRIGGER by falling edge */ + ctx->spi->CONREG = (ctx->spi->CONREG & ~(0b1 << 3)) | (0b1 << 3); /* enable auto trigger */ + + /* clang-format off */ + ctx->spi->DMAREG = ( + 0 + | (0x01 << 23) /* RXDEN = 1 */ + | (15 << 16) /* RX_THRESHOLD = 15 */ + | (0x01 << 7) /* TXDEN = 1 */ + | (0 << 0) /* TX_THRESHOLD = 0 */ + ); + /* clang-format on */ + + dma_enable(); + + time_t now; + gettime(&now, NULL); + const time_t start = now; + + while (pendingTerm == 0) { + if (ctx->crcErrors || true) { + if ((ctx->crcErrors > 0) || (ctx->valueErrors > 0)) { + gettime(&now, NULL); + fprintf(stdout, LOG_TAG "[%llu] crcErrors:%u valueErrors:%u\n", (long long unsigned)((now - start) / 1000), ctx->crcErrors, ctx->valueErrors); + fflush(stderr); + } + } + sleep(60); + }; + + fprintf(stderr, "dma_disable()\n"); + dma_disable(); + usleep(200000); + fprintf(stderr, "resetEcspi()\n"); + resetEcspi(ctx->spi); + + return 0; +} diff --git a/adc/ade9113/msgapi.c b/adc/ade9113/msgapi.c new file mode 100644 index 000000000..fb8aadd24 --- /dev/null +++ b/adc/ade9113/msgapi.c @@ -0,0 +1,151 @@ +/* + * Phoenix-RTOS + * + * Basic sample server over messages + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "ADE9113-API: " + +#include "log.h" +#include "sample.h" +#include "msgapi.h" + + +int msgapi_init(struct msgapiCtx *ctx, struct sampleCtx *sample, const char *name) +{ + *ctx = (struct msgapiCtx) { + .port = UINT32_MAX, + }; + + mutexCreate(&ctx->lock); + + for (unsigned ch = 0; ch < MSGAPI_READER_COUNT; ++ch) { + sample_readerInit(&ctx->readers[ch], sample); + } + + int res = portCreate(&ctx->port); + if (res != EOK) { + log_error("could not create port: %d", res); + return -1; + } + + oid_t dev = { .id = 0, .port = ctx->port }; + res = create_dev(&dev, name); + if (res != EOK) { + log_error("could not create device ade9113"); + return -1; + } + + return 0; +} + + +int msgapi_serve(struct msgapiCtx *ctx) +{ + while (true) { + msg_rid_t rid; + msg_t msg; + if (msgRecv(ctx->port, &msg, &rid) < 0) { + continue; + } + struct sampleReader *reader = NULL; + if ((msg.oid.id > 0) && (msg.oid.id <= MSGAPI_READER_COUNT)) { + reader = &ctx->readers[msg.oid.id - 1]; + } + + switch (msg.type) { + case mtOpen: { + if (msg.oid.id != 0) { + msg.o.err = -EBADF; + break; + } + msg.o.err = -EBUSY; + + if (mutexLock(ctx->lock) < 0) { + msg.o.err = -EIO; + break; + } + for (unsigned ch = 0; ch < MSGAPI_READER_COUNT; ++ch) { + if (!ctx->readers[ch].open) { + reader = &ctx->readers[ch]; + /* + * TODO: consider adding option to read already queued samples + * For now only new samples (collected after open) can be read + */ + sample_readerOpen(reader); + ctx->opened += 1; + msg.o.err = ch + 1; /* nonnegative err from open is to pass oid.id */ + log_info("new msgapi session: ch=%d pid=%d opened=%u\n", ch + 1, msg.pid, ctx->opened); + break; + } + } + mutexUnlock(ctx->lock); + + if (msg.o.err == -EBUSY) { + log_error("failed to allocate msgapi session: pid=%d opened=%u\n", msg.pid, ctx->opened); + } + break; + } + + case mtClose: { + if (reader == NULL) { + msg.o.err = -EBADF; + break; + } + + if (mutexLock(ctx->lock) < 0) { + msg.o.err = -EIO; + break; + } + sample_readerClose(reader); + msg.o.err = EOK; + ctx->opened -= 1; + log_info("closing msgapi session: ch=%d pid=%d opened=%u\n", (int)msg.oid.id, msg.pid, ctx->opened); + mutexUnlock(ctx->lock); + break; + } + + case mtGetAttr: + case mtSetAttr: + msg.o.err = EOK; + break; + + case mtRead: { + if (reader == NULL) { + msg.o.err = -EBADF; + break; + } + /* TODO: create library for implementing async blocking queue reads (see klog reading) and use it here */ + msg.o.err = sample_readBlock(reader, msg.o.data, msg.o.size); + break; + } + + case mtWrite: { + msg.o.err = -ENOSYS; + break; + } + + default: + msg.o.err = -ENOSYS; + break; + } + msgRespond(ctx->port, &msg, rid); + } +} diff --git a/adc/ade9113/msgapi.h b/adc/ade9113/msgapi.h new file mode 100644 index 000000000..6e951edfb --- /dev/null +++ b/adc/ade9113/msgapi.h @@ -0,0 +1,34 @@ +/* + * Phoenix-RTOS + * + * Basic sample server over messages + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#ifndef MSGAPI_H +#define MSGAPI_H + +#include "sample.h" + +#define MSGAPI_READER_COUNT 4 + +struct msgapiCtx { + uint32_t port; + handle_t lock; + + struct sampleReader readers[MSGAPI_READER_COUNT]; + unsigned opened; +}; + +int msgapi_init(struct msgapiCtx *msgapi, struct sampleCtx *ctx, const char *name); + + +int msgapi_serve(struct msgapiCtx *ctx); + + +#endif /* end of include guard: MSGAPI_H */ diff --git a/adc/ade9113/sample.c b/adc/ade9113/sample.c new file mode 100644 index 000000000..88a712aa5 --- /dev/null +++ b/adc/ade9113/sample.c @@ -0,0 +1,189 @@ +/* + * Phoenix-RTOS + * + * Simple ring buffer with multiple readers and fixed sample size + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include +#include +#include +#include + +#define LOG_TAG "ADE9113-SAMPLE: " + +#include "log.h" +#include "sample.h" + + +void sample_writeStart(struct sampleCtx *ctx) +{ + assert(!ctx->writeInProgress); + mutexLock(ctx->lock); + ctx->writeInProgress = true; +} + + +/* non static inline in sample.h */ +extern inline void sample_write(struct sampleCtx *ctx, const uint8_t *data, size_t size); + + +void sample_writeEnd(struct sampleCtx *ctx) +{ + assert(ctx->writeInProgress); + if (ctx->nextWakeup <= ctx->written) { + ctx->nextWakeup = UINT64_MAX; + condBroadcast(ctx->cond); + } + ctx->writeInProgress = false; + mutexUnlock(ctx->lock); +} + + +void sample_readerInit(struct sampleReader *reader, struct sampleCtx *ctx) +{ + mutexLock(ctx->lock); + *reader = (struct sampleReader) { + .ctx = ctx, + .offs = 0, + .open = false + }; + mutexUnlock(ctx->lock); +} + + +void sample_readerOpen(struct sampleReader *reader) +{ + assert(reader->ctx != NULL); + mutexLock(reader->ctx->lock); + reader->open = true; + reader->offs = reader->ctx->written; + /* align initial read offset with sample start */ + reader->offs -= reader->offs % reader->ctx->sampleSize; + mutexUnlock(reader->ctx->lock); +} + + +void sample_readerClose(struct sampleReader *reader) +{ + struct sampleCtx *ctx = reader->ctx; + assert(ctx != NULL); + mutexLock(ctx->lock); + if (reader->open) { + reader->offs = 0; + reader->open = false; + condBroadcast(ctx->cond); + } + mutexUnlock(ctx->lock); +} + + +static ssize_t sampleRead(struct sampleReader *reader, uint8_t *data, size_t size, bool block) +{ + const struct sampleCtx *ctx = reader->ctx; + mutexLock(ctx->lock); + + if (block) { + uint64_t needed = reader->offs + size; + while ((reader->ctx->written < needed) && (reader->open)) { + if (reader->ctx->nextWakeup > needed) { + reader->ctx->nextWakeup = needed; + } + condWait(reader->ctx->cond, reader->ctx->lock, 0); + } + } + if (!reader->open) { + mutexUnlock(ctx->lock); + return -1; + } + + uint64_t readySize = ctx->written - reader->offs; + if (readySize > ctx->mask) { + /* reader too slow - whole buffer was filled */ + mutexUnlock(ctx->lock); + return -1; + } + size = (size > readySize) ? readySize : size; + + uint32_t start = reader->offs & ctx->mask; + uint32_t end = (reader->offs + size) & ctx->mask; + if (start == end) { + // pass + } + else if (start < end) { + memcpy(data, (void *)ctx->buffer + start, end - start); + } + else { + memcpy(data, (void *)ctx->buffer + start, size - end); + memcpy(data + (size - end), (void *)ctx->buffer, end); + } + + mutexUnlock(ctx->lock); + reader->offs += size; + return size; +} + + +ssize_t sample_readBlock(struct sampleReader *reader, uint8_t *data, size_t size) +{ + return sampleRead(reader, data, size, true); +} + + +ssize_t sample_read(struct sampleReader *reader, uint8_t *data, size_t size) +{ + return sampleRead(reader, data, size, false); +} + + +static void ctxCleanup(struct sampleCtx *ctx) +{ + /* NOTE: all function here are are safe (no-op) on default values */ + free(ctx->buffer); + resourceDestroy(ctx->cond); + resourceDestroy(ctx->lock); + *ctx = (struct sampleCtx) { .buffer = NULL }; +} + + +int sample_init(struct sampleCtx *ctx, unsigned expSize, unsigned sampleSize) +{ + if ((expSize > 31) || (((uint32_t)sampleSize >> expSize) > 0)) { + return -1; + } + + *ctx = (struct sampleCtx) { + .written = 0, + .sampleSize = sampleSize, + .mask = ((uint32_t)1 << expSize) - 1, + .buffer = malloc((uint32_t)1 << expSize) + }; + + if (ctx->buffer == NULL) { + log_error("failed to allocate sample buffer"); + ctxCleanup(ctx); + return -1; + } + + if (mutexCreate(&ctx->lock) < 0) { + log_error("mutex create failed"); + assert(false); + ctxCleanup(ctx); + return -1; + } + + if (condCreate(&ctx->cond) < 0) { + log_error("cond create failed"); + assert(false); + ctxCleanup(ctx); + return -1; + } + + return 0; +} diff --git a/adc/ade9113/sample.h b/adc/ade9113/sample.h new file mode 100644 index 000000000..d3b75002c --- /dev/null +++ b/adc/ade9113/sample.h @@ -0,0 +1,81 @@ +/* + * Phoenix-RTOS + * + * Simple ring buffer with multiple readers and fixed sample size + * + * Copyright 2026 Phoenix Systems + * Author: Jan Wiśniewski + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#ifndef SAMPLE_H +#define SAMPLE_H + +#include +#include +#include +#include +#include +#include + + +struct sampleCtx { + /* static configuration */ + unsigned sampleSize; + uint64_t mask; /* mask used for calculation of block size modulo (size is power of 2) */ + + /* modified under lock */ + bool writeInProgress; + uint64_t written; + uint64_t nextWakeup; + + handle_t lock; + handle_t cond; + uint8_t *buffer; +}; + + +/* + * Write sample data chunk/part + * + * This has to be called inside transaction that starts with `sample_writeStart` and ends with `sample_writeEnd`. + * Multiple chunks of data can be written in single transaction. Whole transaction is atomic. Blocked readers are + * notified when sample_writeEnd is called. + */ +inline void sample_write(struct sampleCtx *ctx, const uint8_t *data, size_t size) +{ + assert(ctx->writeInProgress); + for (size_t i = 0; i < size; ++i) { + ctx->buffer[ctx->written & ctx->mask] = data[i]; + ctx->written += 1; + } +} +void sample_writeStart(struct sampleCtx *ctx); +void sample_writeEnd(struct sampleCtx *ctx); + + +struct sampleReader { + struct sampleCtx *ctx; + uint64_t offs; + bool open; +}; + +/* initialize reader. First read will start with first sample received after readerInit is called */ +void sample_readerInit(struct sampleReader *reader, struct sampleCtx *ctx); +void sample_readerOpen(struct sampleReader *reader); +void sample_readerClose(struct sampleReader *reader); +ssize_t sample_readBlock(struct sampleReader *reader, uint8_t *data, size_t size); +ssize_t sample_read(struct sampleReader *reader, uint8_t *data, size_t size); + +/* + * Initialize and allocate sample buffer + * + * @param[in] sampleSize size in bytes of single sample. New readers will start with offset aligned to this value + * @param[in] expSize size of ring buffer as exponent of two (size = 2^expSize) + */ +int sample_init(struct sampleCtx *ctx, unsigned expSize, unsigned sampleSize); + + +#endif /* end of include guard: SAMPLE_H */